diff options
Diffstat (limited to 'extern/mantaflow/preprocessed/plugin')
16 files changed, 21044 insertions, 0 deletions
diff --git a/extern/mantaflow/preprocessed/plugin/advection.cpp b/extern/mantaflow/preprocessed/plugin/advection.cpp new file mode 100644 index 00000000000..13f53140348 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/advection.cpp @@ -0,0 +1,1521 @@ + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2011-2015 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 advection + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "grid.h" +#include "kernel.h" +#include <limits> + +using namespace std; + +namespace Manta { + +//! Semi-Lagrange interpolation kernel + +template<class T> struct SemiLagrange : public KernelBase { + SemiLagrange(const FlagGrid &flags, + const MACGrid &vel, + Grid<T> &dst, + const Grid<T> &src, + Real dt, + bool isLevelset, + int orderSpace, + int orderTrace) + : KernelBase(&flags, 1), + flags(flags), + vel(vel), + dst(dst), + src(src), + dt(dt), + isLevelset(isLevelset), + orderSpace(orderSpace), + orderTrace(orderTrace) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &vel, + Grid<T> &dst, + const Grid<T> &src, + Real dt, + bool isLevelset, + int orderSpace, + int orderTrace) const + { + if (orderTrace == 1) { + // traceback position + Vec3 pos = Vec3(i + 0.5f, j + 0.5f, k + 0.5f) - vel.getCentered(i, j, k) * dt; + dst(i, j, k) = src.getInterpolatedHi(pos, orderSpace); + } + else if (orderTrace == 2) { + // backtracing using explicit midpoint + Vec3 p0 = Vec3(i + 0.5f, j + 0.5f, k + 0.5f); + Vec3 p1 = p0 - vel.getCentered(i, j, k) * dt * 0.5; + Vec3 p2 = p0 - vel.getInterpolated(p1) * dt; + dst(i, j, k) = src.getInterpolatedHi(p2, orderSpace); + } + else { + assertMsg(false, "Unknown backtracing order " << orderTrace); + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline Grid<T> &getArg2() + { + return dst; + } + typedef Grid<T> type2; + inline const Grid<T> &getArg3() + { + return src; + } + typedef Grid<T> type3; + inline Real &getArg4() + { + return dt; + } + typedef Real type4; + inline bool &getArg5() + { + return isLevelset; + } + typedef bool type5; + inline int &getArg6() + { + return orderSpace; + } + typedef int type6; + inline int &getArg7() + { + return orderTrace; + } + typedef int type7; + void runMessage() + { + debMsg("Executing kernel SemiLagrange ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, src, dt, isLevelset, orderSpace, orderTrace); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, src, dt, isLevelset, orderSpace, orderTrace); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &vel; + Grid<T> &dst; + const Grid<T> &src; + Real dt; + bool isLevelset; + int orderSpace; + int orderTrace; +}; + +//! Semi-Lagrange interpolation kernel for MAC grids + +struct SemiLagrangeMAC : public KernelBase { + SemiLagrangeMAC(const FlagGrid &flags, + const MACGrid &vel, + MACGrid &dst, + const MACGrid &src, + Real dt, + int orderSpace, + int orderTrace) + : KernelBase(&flags, 1), + flags(flags), + vel(vel), + dst(dst), + src(src), + dt(dt), + orderSpace(orderSpace), + orderTrace(orderTrace) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &vel, + MACGrid &dst, + const MACGrid &src, + Real dt, + int orderSpace, + int orderTrace) const + { + if (orderTrace == 1) { + // get currect velocity at MAC position + // no need to shift xpos etc. as lookup field is also shifted + Vec3 xpos = Vec3(i + 0.5f, j + 0.5f, k + 0.5f) - vel.getAtMACX(i, j, k) * dt; + Real vx = src.getInterpolatedComponentHi<0>(xpos, orderSpace); + Vec3 ypos = Vec3(i + 0.5f, j + 0.5f, k + 0.5f) - vel.getAtMACY(i, j, k) * dt; + Real vy = src.getInterpolatedComponentHi<1>(ypos, orderSpace); + Vec3 zpos = Vec3(i + 0.5f, j + 0.5f, k + 0.5f) - vel.getAtMACZ(i, j, k) * dt; + Real vz = src.getInterpolatedComponentHi<2>(zpos, orderSpace); + + dst(i, j, k) = Vec3(vx, vy, vz); + } + else if (orderTrace == 2) { + Vec3 p0 = Vec3(i + 0.5, j + 0.5, k + 0.5); + Vec3 xp0 = Vec3(i, j + 0.5f, k + 0.5f); + Vec3 xp1 = xp0 - src.getAtMACX(i, j, k) * dt * 0.5; + Vec3 xp2 = p0 - src.getInterpolated(xp1) * dt; + Real vx = src.getInterpolatedComponentHi<0>(xp2, orderSpace); + Vec3 yp0 = Vec3(i + 0.5f, j, k + 0.5f); + Vec3 yp1 = yp0 - src.getAtMACY(i, j, k) * dt * 0.5; + Vec3 yp2 = p0 - src.getInterpolated(yp1) * dt; + Real vy = src.getInterpolatedComponentHi<1>(yp2, orderSpace); + Vec3 zp0 = Vec3(i + 0.5f, j + 0.5f, k); + Vec3 zp1 = zp0 - src.getAtMACZ(i, j, k) * dt * 0.5; + Vec3 zp2 = p0 - src.getInterpolated(zp1) * dt; + Real vz = src.getInterpolatedComponentHi<2>(zp2, orderSpace); + + dst(i, j, k) = Vec3(vx, vy, vz); + } + else { + assertMsg(false, "Unknown backtracing order " << orderTrace); + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline MACGrid &getArg2() + { + return dst; + } + typedef MACGrid type2; + inline const MACGrid &getArg3() + { + return src; + } + typedef MACGrid type3; + inline Real &getArg4() + { + return dt; + } + typedef Real type4; + inline int &getArg5() + { + return orderSpace; + } + typedef int type5; + inline int &getArg6() + { + return orderTrace; + } + typedef int type6; + void runMessage() + { + debMsg("Executing kernel SemiLagrangeMAC ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, src, dt, orderSpace, orderTrace); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, src, dt, orderSpace, orderTrace); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &vel; + MACGrid &dst; + const MACGrid &src; + Real dt; + int orderSpace; + int orderTrace; +}; + +//! Kernel: Correct based on forward and backward SL steps (for both centered & mac grids) + +template<class T> struct MacCormackCorrect : public KernelBase { + MacCormackCorrect(const FlagGrid &flags, + Grid<T> &dst, + const Grid<T> &old, + const Grid<T> &fwd, + const Grid<T> &bwd, + Real strength, + bool isLevelSet, + bool isMAC = false) + : KernelBase(&flags, 0), + flags(flags), + dst(dst), + old(old), + fwd(fwd), + bwd(bwd), + strength(strength), + isLevelSet(isLevelSet), + isMAC(isMAC) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const FlagGrid &flags, + Grid<T> &dst, + const Grid<T> &old, + const Grid<T> &fwd, + const Grid<T> &bwd, + Real strength, + bool isLevelSet, + bool isMAC = false) const + { + dst[idx] = fwd[idx]; + + if (flags.isFluid(idx)) { + // only correct inside fluid region; note, strenth of correction can be modified here + dst[idx] += strength * 0.5 * (old[idx] - bwd[idx]); + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<T> &getArg1() + { + return dst; + } + typedef Grid<T> type1; + inline const Grid<T> &getArg2() + { + return old; + } + typedef Grid<T> type2; + inline const Grid<T> &getArg3() + { + return fwd; + } + typedef Grid<T> type3; + inline const Grid<T> &getArg4() + { + return bwd; + } + typedef Grid<T> type4; + inline Real &getArg5() + { + return strength; + } + typedef Real type5; + inline bool &getArg6() + { + return isLevelSet; + } + typedef bool type6; + inline bool &getArg7() + { + return isMAC; + } + typedef bool type7; + void runMessage() + { + debMsg("Executing kernel MacCormackCorrect ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, flags, dst, old, fwd, bwd, strength, isLevelSet, isMAC); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const FlagGrid &flags; + Grid<T> &dst; + const Grid<T> &old; + const Grid<T> &fwd; + const Grid<T> &bwd; + Real strength; + bool isLevelSet; + bool isMAC; +}; + +//! Kernel: Correct based on forward and backward SL steps (for both centered & mac grids) + +template<class T> struct MacCormackCorrectMAC : public KernelBase { + MacCormackCorrectMAC(const FlagGrid &flags, + Grid<T> &dst, + const Grid<T> &old, + const Grid<T> &fwd, + const Grid<T> &bwd, + Real strength, + bool isLevelSet, + bool isMAC = false) + : KernelBase(&flags, 0), + flags(flags), + dst(dst), + old(old), + fwd(fwd), + bwd(bwd), + strength(strength), + isLevelSet(isLevelSet), + isMAC(isMAC) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<T> &dst, + const Grid<T> &old, + const Grid<T> &fwd, + const Grid<T> &bwd, + Real strength, + bool isLevelSet, + bool isMAC = false) const + { + bool skip[3] = {false, false, false}; + + if (!flags.isFluid(i, j, k)) + skip[0] = skip[1] = skip[2] = true; + if (isMAC) { + if ((i > 0) && (!flags.isFluid(i - 1, j, k))) + skip[0] = true; + if ((j > 0) && (!flags.isFluid(i, j - 1, k))) + skip[1] = true; + if ((k > 0) && (!flags.isFluid(i, j, k - 1))) + skip[2] = true; + } + + for (int c = 0; c < 3; ++c) { + if (skip[c]) { + dst(i, j, k)[c] = fwd(i, j, k)[c]; + } + else { + // perform actual correction with given strength + dst(i, j, k)[c] = fwd(i, j, k)[c] + strength * 0.5 * (old(i, j, k)[c] - bwd(i, j, k)[c]); + } + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<T> &getArg1() + { + return dst; + } + typedef Grid<T> type1; + inline const Grid<T> &getArg2() + { + return old; + } + typedef Grid<T> type2; + inline const Grid<T> &getArg3() + { + return fwd; + } + typedef Grid<T> type3; + inline const Grid<T> &getArg4() + { + return bwd; + } + typedef Grid<T> type4; + inline Real &getArg5() + { + return strength; + } + typedef Real type5; + inline bool &getArg6() + { + return isLevelSet; + } + typedef bool type6; + inline bool &getArg7() + { + return isMAC; + } + typedef bool type7; + void runMessage() + { + debMsg("Executing kernel MacCormackCorrectMAC ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, dst, old, fwd, bwd, strength, isLevelSet, isMAC); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, dst, old, fwd, bwd, strength, isLevelSet, isMAC); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<T> &dst; + const Grid<T> &old; + const Grid<T> &fwd; + const Grid<T> &bwd; + Real strength; + bool isLevelSet; + bool isMAC; +}; + +// Helper to collect min/max in a template +template<class T> inline void getMinMax(T &minv, T &maxv, const T &val) +{ + if (val < minv) + minv = val; + if (val > maxv) + maxv = val; +} +template<> inline void getMinMax<Vec3>(Vec3 &minv, Vec3 &maxv, const Vec3 &val) +{ + getMinMax(minv.x, maxv.x, val.x); + getMinMax(minv.y, maxv.y, val.y); + getMinMax(minv.z, maxv.z, val.z); +} + +//! detect out of bounds value +template<class T> inline bool cmpMinMax(T &minv, T &maxv, const T &val) +{ + if (val < minv) + return true; + if (val > maxv) + return true; + return false; +} +template<> inline bool cmpMinMax<Vec3>(Vec3 &minv, Vec3 &maxv, const Vec3 &val) +{ + return (cmpMinMax(minv.x, maxv.x, val.x) | cmpMinMax(minv.y, maxv.y, val.y) | + cmpMinMax(minv.z, maxv.z, val.z)); +} + +#define checkFlag(x, y, z) (flags((x), (y), (z)) & (FlagGrid::TypeFluid | FlagGrid::TypeEmpty)) + +//! Helper function for clamping non-mac grids (those have specialized per component version below) +// Note - 2 clamp modes, a sharper one (default, clampMode 1, also uses backward step), +// and a softer version (clampMode 2) that is recommended in Andy's paper +template<class T> +inline T doClampComponent(const Vec3i &gridSize, + const FlagGrid &flags, + T dst, + const Grid<T> &orig, + const T fwd, + const Vec3 &pos, + const Vec3 &vel, + const int clampMode) +{ + T minv(std::numeric_limits<Real>::max()), maxv(-std::numeric_limits<Real>::max()); + bool haveFl = false; + + // forward (and optionally) backward + Vec3i positions[2]; + int numPos = 1; + positions[0] = toVec3i(pos - vel); + if (clampMode == 1) { + numPos = 2; + positions[1] = toVec3i(pos + vel); + } + + for (int l = 0; l < numPos; ++l) { + Vec3i &currPos = positions[l]; + + // clamp lookup to grid + const int i0 = clamp(currPos.x, 0, gridSize.x - 1); // note! gridsize already has -1 from call + const int j0 = clamp(currPos.y, 0, gridSize.y - 1); + const int k0 = clamp(currPos.z, 0, (orig.is3D() ? (gridSize.z - 1) : 1)); + const int i1 = i0 + 1, j1 = j0 + 1, k1 = (orig.is3D() ? (k0 + 1) : k0); + + // find min/max around source pos + if (checkFlag(i0, j0, k0)) { + getMinMax(minv, maxv, orig(i0, j0, k0)); + haveFl = true; + } + if (checkFlag(i1, j0, k0)) { + getMinMax(minv, maxv, orig(i1, j0, k0)); + haveFl = true; + } + if (checkFlag(i0, j1, k0)) { + getMinMax(minv, maxv, orig(i0, j1, k0)); + haveFl = true; + } + if (checkFlag(i1, j1, k0)) { + getMinMax(minv, maxv, orig(i1, j1, k0)); + haveFl = true; + } + + if (orig.is3D()) { + if (checkFlag(i0, j0, k1)) { + getMinMax(minv, maxv, orig(i0, j0, k1)); + haveFl = true; + } + if (checkFlag(i1, j0, k1)) { + getMinMax(minv, maxv, orig(i1, j0, k1)); + haveFl = true; + } + if (checkFlag(i0, j1, k1)) { + getMinMax(minv, maxv, orig(i0, j1, k1)); + haveFl = true; + } + if (checkFlag(i1, j1, k1)) { + getMinMax(minv, maxv, orig(i1, j1, k1)); + haveFl = true; + } + } + } + + if (!haveFl) + return fwd; + if (clampMode == 1) { + dst = clamp(dst, minv, maxv); // hard clamp + } + else { + if (cmpMinMax(minv, maxv, dst)) + dst = fwd; // recommended in paper, "softer" + } + return dst; +} + +//! Helper function for clamping MAC grids, slight differences in flag checks +// similar to scalar version, just uses single component c of vec3 values +// for symmetry, reverts to first order near boundaries for clampMode 2 +template<int c> +inline Real doClampComponentMAC(const FlagGrid &flags, + const Vec3i &gridSize, + Real dst, + const MACGrid &orig, + Real fwd, + const Vec3 &pos, + const Vec3 &vel, + const int clampMode) +{ + Real minv = std::numeric_limits<Real>::max(), maxv = -std::numeric_limits<Real>::max(); + // bool haveFl = false; + + // forward (and optionally) backward + Vec3i positions[2]; + int numPos = 1; + positions[0] = toVec3i(pos - vel); + if (clampMode == 1) { + numPos = 2; + positions[1] = toVec3i(pos + vel); + } + + Vec3i oPos = toVec3i(pos); + Vec3i nbPos = oPos; + nbPos[c] -= 1; + if (clampMode == 2 && + (!(checkFlag(oPos.x, oPos.y, oPos.z) && checkFlag(nbPos.x, nbPos.y, nbPos.z)))) + return fwd; // replaces haveFl check + + for (int l = 0; l < numPos; ++l) { + Vec3i &currPos = positions[l]; + + const int i0 = clamp(currPos.x, 0, gridSize.x - 1); // note! gridsize already has -1 from call + const int j0 = clamp( + currPos.y, 0, gridSize.y - 1); // but we need a clamp to -2 for the +1 offset below + const int k0 = clamp(currPos.z, 0, (orig.is3D() ? (gridSize.z - 1) : 0)); + const int i1 = i0 + 1, j1 = j0 + 1, k1 = (orig.is3D() ? (k0 + 1) : k0); + + // find min/max around source pos + getMinMax(minv, maxv, orig(i0, j0, k0)[c]); + getMinMax(minv, maxv, orig(i1, j0, k0)[c]); + getMinMax(minv, maxv, orig(i0, j1, k0)[c]); + getMinMax(minv, maxv, orig(i1, j1, k0)[c]); + + if (orig.is3D()) { + getMinMax(minv, maxv, orig(i0, j0, k1)[c]); + getMinMax(minv, maxv, orig(i1, j0, k1)[c]); + getMinMax(minv, maxv, orig(i0, j1, k1)[c]); + getMinMax(minv, maxv, orig(i1, j1, k1)[c]); + } + } + + if (clampMode == 1) { + dst = clamp(dst, minv, maxv); // hard clamp + } + else { + if (cmpMinMax(minv, maxv, dst)) + dst = fwd; // recommended in paper, "softer" + } + return dst; +} + +#undef checkFlag + +//! Kernel: Clamp obtained value to min/max in source area, and reset values that point out of grid +//! or into boundaries +// (note - MAC grids are handled below) + +template<class T> struct MacCormackClamp : public KernelBase { + MacCormackClamp(const FlagGrid &flags, + const MACGrid &vel, + Grid<T> &dst, + const Grid<T> &orig, + const Grid<T> &fwd, + Real dt, + const int clampMode) + : KernelBase(&flags, 1), + flags(flags), + vel(vel), + dst(dst), + orig(orig), + fwd(fwd), + dt(dt), + clampMode(clampMode) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &vel, + Grid<T> &dst, + const Grid<T> &orig, + const Grid<T> &fwd, + Real dt, + const int clampMode) const + { + T dval = dst(i, j, k); + Vec3i gridUpper = flags.getSize() - 1; + + dval = doClampComponent<T>(gridUpper, + flags, + dval, + orig, + fwd(i, j, k), + Vec3(i, j, k), + vel.getCentered(i, j, k) * dt, + clampMode); + + if (1 && clampMode == 1) { + // lookup forward/backward , round to closest NB + Vec3i posFwd = toVec3i(Vec3(i, j, k) + Vec3(0.5, 0.5, 0.5) - vel.getCentered(i, j, k) * dt); + Vec3i posBwd = toVec3i(Vec3(i, j, k) + Vec3(0.5, 0.5, 0.5) + vel.getCentered(i, j, k) * dt); + + // test if lookups point out of grid or into obstacle (note doClampComponent already checks + // sides, below is needed for valid flags access) + if (posFwd.x < 0 || posFwd.y < 0 || posFwd.z < 0 || posBwd.x < 0 || posBwd.y < 0 || + posBwd.z < 0 || posFwd.x > gridUpper.x || posFwd.y > gridUpper.y || + ((posFwd.z > gridUpper.z) && flags.is3D()) || posBwd.x > gridUpper.x || + posBwd.y > gridUpper.y || ((posBwd.z > gridUpper.z) && flags.is3D()) || + flags.isObstacle(posFwd) || flags.isObstacle(posBwd)) { + dval = fwd(i, j, k); + } + } + // clampMode 2 handles flags in doClampComponent call + + dst(i, j, k) = dval; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline Grid<T> &getArg2() + { + return dst; + } + typedef Grid<T> type2; + inline const Grid<T> &getArg3() + { + return orig; + } + typedef Grid<T> type3; + inline const Grid<T> &getArg4() + { + return fwd; + } + typedef Grid<T> type4; + inline Real &getArg5() + { + return dt; + } + typedef Real type5; + inline const int &getArg6() + { + return clampMode; + } + typedef int type6; + void runMessage() + { + debMsg("Executing kernel MacCormackClamp ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, orig, fwd, dt, clampMode); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, orig, fwd, dt, clampMode); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &vel; + Grid<T> &dst; + const Grid<T> &orig; + const Grid<T> &fwd; + Real dt; + const int clampMode; +}; + +//! Kernel: same as MacCormackClamp above, but specialized version for MAC grids + +struct MacCormackClampMAC : public KernelBase { + MacCormackClampMAC(const FlagGrid &flags, + const MACGrid &vel, + MACGrid &dst, + const MACGrid &orig, + const MACGrid &fwd, + Real dt, + const int clampMode) + : KernelBase(&flags, 1), + flags(flags), + vel(vel), + dst(dst), + orig(orig), + fwd(fwd), + dt(dt), + clampMode(clampMode) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &vel, + MACGrid &dst, + const MACGrid &orig, + const MACGrid &fwd, + Real dt, + const int clampMode) const + { + Vec3 pos(i, j, k); + Vec3 dval = dst(i, j, k); + Vec3 dfwd = fwd(i, j, k); + Vec3i gridUpper = flags.getSize() - 1; + + dval.x = doClampComponentMAC<0>( + flags, gridUpper, dval.x, orig, dfwd.x, pos, vel.getAtMACX(i, j, k) * dt, clampMode); + dval.y = doClampComponentMAC<1>( + flags, gridUpper, dval.y, orig, dfwd.y, pos, vel.getAtMACY(i, j, k) * dt, clampMode); + if (flags.is3D()) + dval.z = doClampComponentMAC<2>( + flags, gridUpper, dval.z, orig, dfwd.z, pos, vel.getAtMACZ(i, j, k) * dt, clampMode); + + // note - the MAC version currently does not check whether source points were inside an + // obstacle! (unlike centered version) this would need to be done for each face separately to + // stay symmetric... + + dst(i, j, k) = dval; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline MACGrid &getArg2() + { + return dst; + } + typedef MACGrid type2; + inline const MACGrid &getArg3() + { + return orig; + } + typedef MACGrid type3; + inline const MACGrid &getArg4() + { + return fwd; + } + typedef MACGrid type4; + inline Real &getArg5() + { + return dt; + } + typedef Real type5; + inline const int &getArg6() + { + return clampMode; + } + typedef int type6; + void runMessage() + { + debMsg("Executing kernel MacCormackClampMAC ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, orig, fwd, dt, clampMode); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, dst, orig, fwd, dt, clampMode); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &vel; + MACGrid &dst; + const MACGrid &orig; + const MACGrid &fwd; + Real dt; + const int clampMode; +}; + +//! template function for performing SL advection +//! (Note boundary width only needed for specialization for MAC grids below) +template<class GridType> +void fnAdvectSemiLagrange(FluidSolver *parent, + const FlagGrid &flags, + const MACGrid &vel, + GridType &orig, + int order, + Real strength, + int orderSpace, + int clampMode, + int orderTrace) +{ + typedef typename GridType::BASETYPE T; + + Real dt = parent->getDt(); + bool levelset = orig.getType() & GridBase::TypeLevelset; + + // forward step + GridType fwd(parent); + SemiLagrange<T>(flags, vel, fwd, orig, dt, levelset, orderSpace, orderTrace); + + if (order == 1) { + orig.swap(fwd); + } + else if (order == 2) { // MacCormack + GridType bwd(parent); + GridType newGrid(parent); + + // bwd <- backwards step + SemiLagrange<T>(flags, vel, bwd, fwd, -dt, levelset, orderSpace, orderTrace); + + // newGrid <- compute correction + MacCormackCorrect<T>(flags, newGrid, orig, fwd, bwd, strength, levelset); + + // clamp values + MacCormackClamp<T>(flags, vel, newGrid, orig, fwd, dt, clampMode); + + orig.swap(newGrid); + } +} + +// outflow functions + +//! calculate local propagation velocity for cell (i,j,k) +Vec3 getBulkVel(const FlagGrid &flags, const MACGrid &vel, int i, int j, int k) +{ + Vec3 avg = Vec3(0.); + int count = 0; + int size = 1; // stencil size + int nmax = (flags.is3D() ? size : 0); + // average the neighboring fluid / outflow cell's velocity + for (int n = -nmax; n <= nmax; n++) { + for (int m = -size; m <= size; m++) { + for (int l = -size; l <= size; l++) { + if (flags.isInBounds(Vec3i(i + l, j + m, k + n)) && + (flags.isFluid(i + l, j + m, k + n) || flags.isOutflow(i + l, j + m, k + n))) { + avg += vel(i + l, j + m, k + n); + count++; + } + } + } + } + return count > 0 ? avg / count : avg; +} + +//! extrapolate normal velocity components into outflow cell +struct extrapolateVelConvectiveBC : public KernelBase { + extrapolateVelConvectiveBC(const FlagGrid &flags, + const MACGrid &vel, + MACGrid &velDst, + const MACGrid &velPrev, + Real timeStep) + : KernelBase(&flags, 0), + flags(flags), + vel(vel), + velDst(velDst), + velPrev(velPrev), + timeStep(timeStep) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &vel, + MACGrid &velDst, + const MACGrid &velPrev, + Real timeStep) const + { + if (flags.isOutflow(i, j, k)) { + Vec3 bulkVel = getBulkVel(flags, vel, i, j, k); + int dim = flags.is3D() ? 3 : 2; + const Vec3i cur = Vec3i(i, j, k); + Vec3i low, up, flLow, flUp; + int cnt = 0; + // iterate over each velocity component x, y, z + for (int c = 0; c < dim; c++) { + low = up = flLow = flUp = cur; + Real factor = timeStep * + max((Real)1.0, bulkVel[c]); // prevent the extrapolated velocity from + // exploding when bulk velocity below 1 + low[c] = flLow[c] = cur[c] - 1; + up[c] = flUp[c] = cur[c] + 1; + // iterate over bWidth to allow for extrapolation into more distant outflow cells; + // hard-coded extrapolation distance of two cells + for (int d = 0; d < 2; d++) { + bool extrapolateFromLower = flags.isInBounds(flLow) && flags.isFluid(flLow); + bool extrapolateFromUpper = flags.isInBounds(flUp) && flags.isFluid(flUp); + if (extrapolateFromLower || extrapolateFromUpper) { + if (extrapolateFromLower) { + velDst(i, j, k) += ((vel(i, j, k) - velPrev(i, j, k)) / factor) + vel(low); + cnt++; + } + if (extrapolateFromUpper) { + // check for cells equally far away from two fluid cells -> average value between + // both sides + velDst(i, j, k) += ((vel(i, j, k) - velPrev(i, j, k)) / factor) + vel(up); + cnt++; + } + break; + } + flLow[c]--; + flUp[c]++; + } + } + if (cnt > 0) + velDst(i, j, k) /= cnt; + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline MACGrid &getArg2() + { + return velDst; + } + typedef MACGrid type2; + inline const MACGrid &getArg3() + { + return velPrev; + } + typedef MACGrid type3; + inline Real &getArg4() + { + return timeStep; + } + typedef Real type4; + void runMessage() + { + debMsg("Executing kernel extrapolateVelConvectiveBC ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, velDst, velPrev, timeStep); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, velDst, velPrev, timeStep); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &vel; + MACGrid &velDst; + const MACGrid &velPrev; + Real timeStep; +}; + +//! copy extrapolated velocity components +struct copyChangedVels : public KernelBase { + copyChangedVels(const FlagGrid &flags, const MACGrid &velDst, MACGrid &vel) + : KernelBase(&flags, 0), flags(flags), velDst(velDst), vel(vel) + { + runMessage(); + run(); + } + inline void op( + int i, int j, int k, const FlagGrid &flags, const MACGrid &velDst, MACGrid &vel) const + { + if (flags.isOutflow(i, j, k)) + vel(i, j, k) = velDst(i, j, k); + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return velDst; + } + typedef MACGrid type1; + inline MACGrid &getArg2() + { + return vel; + } + typedef MACGrid type2; + void runMessage() + { + debMsg("Executing kernel copyChangedVels ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, velDst, vel); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, velDst, vel); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &velDst; + MACGrid &vel; +}; + +//! extrapolate normal velocity components into open boundary cells (marked as outflow cells) +void applyOutflowBC(const FlagGrid &flags, MACGrid &vel, const MACGrid &velPrev, double timeStep) +{ + MACGrid velDst(vel.getParent()); // do not overwrite vel while it is read + extrapolateVelConvectiveBC(flags, vel, velDst, velPrev, max(1.0, timeStep * 4)); + copyChangedVels(flags, velDst, vel); +} + +// advection helpers + +//! prevent parts of the surface getting "stuck" in obstacle regions +struct knResetPhiInObs : public KernelBase { + knResetPhiInObs(const FlagGrid &flags, Grid<Real> &sdf) + : KernelBase(&flags, 0), flags(flags), sdf(sdf) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const FlagGrid &flags, Grid<Real> &sdf) const + { + if (flags.isObstacle(i, j, k) && (sdf(i, j, k) < 0.)) { + sdf(i, j, k) = 0.1; + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return sdf; + } + typedef Grid<Real> type1; + void runMessage() + { + debMsg("Executing kernel knResetPhiInObs ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, sdf); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, sdf); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> &sdf; +}; +void resetPhiInObs(const FlagGrid &flags, Grid<Real> &sdf) +{ + knResetPhiInObs(flags, sdf); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "resetPhiInObs", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &sdf = *_args.getPtr<Grid<Real>>("sdf", 1, &_lock); + _retval = getPyNone(); + resetPhiInObs(flags, sdf); + _args.check(); + } + pbFinalizePlugin(parent, "resetPhiInObs", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("resetPhiInObs", e.what()); + return 0; + } +} +static const Pb::Register _RP_resetPhiInObs("", "resetPhiInObs", _W_0); +extern "C" { +void PbRegister_resetPhiInObs() +{ + KEEP_UNUSED(_RP_resetPhiInObs); +} +} + +// advection main calls + +//! template function for performing SL advection: specialized version for MAC grids +template<> +void fnAdvectSemiLagrange<MACGrid>(FluidSolver *parent, + const FlagGrid &flags, + const MACGrid &vel, + MACGrid &orig, + int order, + Real strength, + int orderSpace, + int clampMode, + int orderTrace) +{ + Real dt = parent->getDt(); + + // forward step + MACGrid fwd(parent); + SemiLagrangeMAC(flags, vel, fwd, orig, dt, orderSpace, orderTrace); + + if (orderSpace != 1) { + debMsg("Warning higher order for MAC grids not yet implemented...", 1); + } + + if (order == 1) { + applyOutflowBC(flags, fwd, orig, dt); + orig.swap(fwd); + } + else if (order == 2) { // MacCormack + MACGrid bwd(parent); + MACGrid newGrid(parent); + + // bwd <- backwards step + SemiLagrangeMAC(flags, vel, bwd, fwd, -dt, orderSpace, orderTrace); + + // newGrid <- compute correction + MacCormackCorrectMAC<Vec3>(flags, newGrid, orig, fwd, bwd, strength, false, true); + + // clamp values + MacCormackClampMAC(flags, vel, newGrid, orig, fwd, dt, clampMode); + + applyOutflowBC(flags, newGrid, orig, dt); + orig.swap(newGrid); + } +} + +//! Perform semi-lagrangian advection of target Real- or Vec3 grid +//! Open boundary handling needs information about width of border +//! Clamping modes: 1 regular clamp leading to more overshoot and sharper results, 2 revert to 1st +//! order slightly smoother less overshoot (enable when 1 gives artifacts) + +void advectSemiLagrange(const FlagGrid *flags, + const MACGrid *vel, + GridBase *grid, + int order = 1, + Real strength = 1.0, + int orderSpace = 1, + bool openBounds = false, + int boundaryWidth = -1, + int clampMode = 2, + int orderTrace = 1) +{ + assertMsg(order == 1 || order == 2, + "AdvectSemiLagrange: Only order 1 (regular SL) and 2 (MacCormack) supported"); + if ((boundaryWidth != -1) || (openBounds)) { + debMsg( + "Warning: boundaryWidth and openBounds parameters in AdvectSemiLagrange plugin are " + "deprecated (and have no more effect), please remove.", + 0); + } + + // determine type of grid + if (grid->getType() & GridBase::TypeReal) { + fnAdvectSemiLagrange<Grid<Real>>(flags->getParent(), + *flags, + *vel, + *((Grid<Real> *)grid), + order, + strength, + orderSpace, + clampMode, + orderTrace); + } + else if (grid->getType() & GridBase::TypeMAC) { + fnAdvectSemiLagrange<MACGrid>(flags->getParent(), + *flags, + *vel, + *((MACGrid *)grid), + order, + strength, + orderSpace, + clampMode, + orderTrace); + } + else if (grid->getType() & GridBase::TypeVec3) { + fnAdvectSemiLagrange<Grid<Vec3>>(flags->getParent(), + *flags, + *vel, + *((Grid<Vec3> *)grid), + order, + strength, + orderSpace, + clampMode, + orderTrace); + } + else + errMsg("AdvectSemiLagrange: Grid Type is not supported (only Real, Vec3, MAC, Levelset)"); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "advectSemiLagrange", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid *flags = _args.getPtr<FlagGrid>("flags", 0, &_lock); + const MACGrid *vel = _args.getPtr<MACGrid>("vel", 1, &_lock); + GridBase *grid = _args.getPtr<GridBase>("grid", 2, &_lock); + int order = _args.getOpt<int>("order", 3, 1, &_lock); + Real strength = _args.getOpt<Real>("strength", 4, 1.0, &_lock); + int orderSpace = _args.getOpt<int>("orderSpace", 5, 1, &_lock); + bool openBounds = _args.getOpt<bool>("openBounds", 6, false, &_lock); + int boundaryWidth = _args.getOpt<int>("boundaryWidth", 7, -1, &_lock); + int clampMode = _args.getOpt<int>("clampMode", 8, 2, &_lock); + int orderTrace = _args.getOpt<int>("orderTrace", 9, 1, &_lock); + _retval = getPyNone(); + advectSemiLagrange(flags, + vel, + grid, + order, + strength, + orderSpace, + openBounds, + boundaryWidth, + clampMode, + orderTrace); + _args.check(); + } + pbFinalizePlugin(parent, "advectSemiLagrange", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("advectSemiLagrange", e.what()); + return 0; + } +} +static const Pb::Register _RP_advectSemiLagrange("", "advectSemiLagrange", _W_1); +extern "C" { +void PbRegister_advectSemiLagrange() +{ + KEEP_UNUSED(_RP_advectSemiLagrange); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/apic.cpp b/extern/mantaflow/preprocessed/plugin/apic.cpp new file mode 100644 index 00000000000..6ff893014c9 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/apic.cpp @@ -0,0 +1,496 @@ + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + +// ---------------------------------------------------------------------------- +// +// MantaFlow fluid solver framework +// Copyright 2016-2017 Kiwon Um, 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 +// +// Affine Particle-In-Cell +// +// ---------------------------------------------------------------------------- + +#include "particle.h" +#include "grid.h" + +namespace Manta { + +struct knApicMapLinearVec3ToMACGrid : public KernelBase { + knApicMapLinearVec3ToMACGrid(const BasicParticleSystem &p, + MACGrid &mg, + MACGrid &vg, + const ParticleDataImpl<Vec3> &vp, + const ParticleDataImpl<Vec3> &cpx, + const ParticleDataImpl<Vec3> &cpy, + const ParticleDataImpl<Vec3> &cpz, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(p.size()), + p(p), + mg(mg), + vg(vg), + vp(vp), + cpx(cpx), + cpy(cpy), + cpz(cpz), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &p, + MACGrid &mg, + MACGrid &vg, + const ParticleDataImpl<Vec3> &vp, + const ParticleDataImpl<Vec3> &cpx, + const ParticleDataImpl<Vec3> &cpy, + const ParticleDataImpl<Vec3> &cpz, + const ParticleDataImpl<int> *ptype, + const int exclude) + { + if (!p.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) + return; + const IndexInt dX[2] = {0, vg.getStrideX()}; + const IndexInt dY[2] = {0, vg.getStrideY()}; + const IndexInt dZ[2] = {0, vg.getStrideZ()}; + + const Vec3 &pos = p[idx].pos, &vel = vp[idx]; + const IndexInt fi = static_cast<IndexInt>(pos.x), fj = static_cast<IndexInt>(pos.y), + fk = static_cast<IndexInt>(pos.z); + const IndexInt ci = static_cast<IndexInt>(pos.x - 0.5), + cj = static_cast<IndexInt>(pos.y - 0.5), + ck = static_cast<IndexInt>(pos.z - 0.5); + const Real wfi = clamp(pos.x - fi, Real(0), Real(1)), + wfj = clamp(pos.y - fj, Real(0), Real(1)), + wfk = clamp(pos.z - fk, Real(0), Real(1)); + const Real wci = clamp(Real(pos.x - ci - 0.5), Real(0), Real(1)), + wcj = clamp(Real(pos.y - cj - 0.5), Real(0), Real(1)), + wck = clamp(Real(pos.z - ck - 0.5), Real(0), Real(1)); + // TODO: check index for safety + { // u-face + const IndexInt gidx = fi * dX[1] + cj * dY[1] + ck * dZ[1]; + const Vec3 gpos(fi, cj + 0.5, ck + 0.5); + const Real wi[2] = {Real(1) - wfi, wfi}; + const Real wj[2] = {Real(1) - wcj, wcj}; + const Real wk[2] = {Real(1) - wck, wck}; + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + for (int k = 0; k < 2; ++k) { + const Real w = wi[i] * wj[j] * wk[k]; + mg[gidx + dX[i] + dY[j] + dZ[k]].x += w; + vg[gidx + dX[i] + dY[j] + dZ[k]].x += w * vel.x; + vg[gidx + dX[i] + dY[j] + dZ[k]].x += w * dot(cpx[idx], gpos + Vec3(i, j, k) - pos); + } + } + { // v-face + const IndexInt gidx = ci * dX[1] + fj * dY[1] + ck * dZ[1]; + const Vec3 gpos(ci + 0.5, fj, ck + 0.5); + const Real wi[2] = {Real(1) - wci, wci}; + const Real wj[2] = {Real(1) - wfj, wfj}; + const Real wk[2] = {Real(1) - wck, wck}; + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + for (int k = 0; k < 2; ++k) { + const Real w = wi[i] * wj[j] * wk[k]; + mg[gidx + dX[i] + dY[j] + dZ[k]].y += w; + vg[gidx + dX[i] + dY[j] + dZ[k]].y += w * vel.y; + vg[gidx + dX[i] + dY[j] + dZ[k]].y += w * dot(cpy[idx], gpos + Vec3(i, j, k) - pos); + } + } + if (!vg.is3D()) + return; + { // w-face + const IndexInt gidx = ci * dX[1] + cj * dY[1] + fk * dZ[1]; + const Vec3 gpos(ci + 0.5, cj + 0.5, fk); + const Real wi[2] = {Real(1) - wci, wci}; + const Real wj[2] = {Real(1) - wcj, wcj}; + const Real wk[2] = {Real(1) - wfk, wfk}; + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + for (int k = 0; k < 2; ++k) { + const Real w = wi[i] * wj[j] * wk[k]; + mg[gidx + dX[i] + dY[j] + dZ[k]].z += w; + vg[gidx + dX[i] + dY[j] + dZ[k]].z += w * vel.z; + vg[gidx + dX[i] + dY[j] + dZ[k]].z += w * dot(cpz[idx], gpos + Vec3(i, j, k) - pos); + } + } + } + inline const BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline MACGrid &getArg1() + { + return mg; + } + typedef MACGrid type1; + inline MACGrid &getArg2() + { + return vg; + } + typedef MACGrid type2; + inline const ParticleDataImpl<Vec3> &getArg3() + { + return vp; + } + typedef ParticleDataImpl<Vec3> type3; + inline const ParticleDataImpl<Vec3> &getArg4() + { + return cpx; + } + typedef ParticleDataImpl<Vec3> type4; + inline const ParticleDataImpl<Vec3> &getArg5() + { + return cpy; + } + typedef ParticleDataImpl<Vec3> type5; + inline const ParticleDataImpl<Vec3> &getArg6() + { + return cpz; + } + typedef ParticleDataImpl<Vec3> type6; + inline const ParticleDataImpl<int> *getArg7() + { + return ptype; + } + typedef ParticleDataImpl<int> type7; + inline const int &getArg8() + { + return exclude; + } + typedef int type8; + void runMessage() + { + debMsg("Executing kernel knApicMapLinearVec3ToMACGrid ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void run() + { + const IndexInt _sz = size; + for (IndexInt i = 0; i < _sz; i++) + op(i, p, mg, vg, vp, cpx, cpy, cpz, ptype, exclude); + } + const BasicParticleSystem &p; + MACGrid &mg; + MACGrid &vg; + const ParticleDataImpl<Vec3> &vp; + const ParticleDataImpl<Vec3> &cpx; + const ParticleDataImpl<Vec3> &cpy; + const ParticleDataImpl<Vec3> &cpz; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; + +void apicMapPartsToMAC(const FlagGrid &flags, + MACGrid &vel, + const BasicParticleSystem &parts, + const ParticleDataImpl<Vec3> &partVel, + const ParticleDataImpl<Vec3> &cpx, + const ParticleDataImpl<Vec3> &cpy, + const ParticleDataImpl<Vec3> &cpz, + MACGrid *mass = NULL, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + // affine map + // let's assume that the particle mass is constant, 1.0 + const bool freeMass = !mass; + if (!mass) + mass = new MACGrid(flags.getParent()); + else + mass->clear(); + + vel.clear(); + knApicMapLinearVec3ToMACGrid(parts, *mass, vel, partVel, cpx, cpy, cpz, ptype, exclude); + mass->stomp(VECTOR_EPSILON); + vel.safeDivide(*mass); + + if (freeMass) + delete mass; +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "apicMapPartsToMAC", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 2, &_lock); + const ParticleDataImpl<Vec3> &partVel = *_args.getPtr<ParticleDataImpl<Vec3>>( + "partVel", 3, &_lock); + const ParticleDataImpl<Vec3> &cpx = *_args.getPtr<ParticleDataImpl<Vec3>>("cpx", 4, &_lock); + const ParticleDataImpl<Vec3> &cpy = *_args.getPtr<ParticleDataImpl<Vec3>>("cpy", 5, &_lock); + const ParticleDataImpl<Vec3> &cpz = *_args.getPtr<ParticleDataImpl<Vec3>>("cpz", 6, &_lock); + MACGrid *mass = _args.getPtrOpt<MACGrid>("mass", 7, NULL, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 8, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 9, 0, &_lock); + _retval = getPyNone(); + apicMapPartsToMAC(flags, vel, parts, partVel, cpx, cpy, cpz, mass, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "apicMapPartsToMAC", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("apicMapPartsToMAC", e.what()); + return 0; + } +} +static const Pb::Register _RP_apicMapPartsToMAC("", "apicMapPartsToMAC", _W_0); +extern "C" { +void PbRegister_apicMapPartsToMAC() +{ + KEEP_UNUSED(_RP_apicMapPartsToMAC); +} +} + +struct knApicMapLinearMACGridToVec3 : public KernelBase { + knApicMapLinearMACGridToVec3(ParticleDataImpl<Vec3> &vp, + ParticleDataImpl<Vec3> &cpx, + ParticleDataImpl<Vec3> &cpy, + ParticleDataImpl<Vec3> &cpz, + const BasicParticleSystem &p, + const MACGrid &vg, + const FlagGrid &flags, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(vp.size()), + vp(vp), + cpx(cpx), + cpy(cpy), + cpz(cpz), + p(p), + vg(vg), + flags(flags), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + ParticleDataImpl<Vec3> &vp, + ParticleDataImpl<Vec3> &cpx, + ParticleDataImpl<Vec3> &cpy, + ParticleDataImpl<Vec3> &cpz, + const BasicParticleSystem &p, + const MACGrid &vg, + const FlagGrid &flags, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + if (!p.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) + return; + + vp[idx] = cpx[idx] = cpy[idx] = cpz[idx] = Vec3(Real(0)); + const IndexInt dX[2] = {0, vg.getStrideX()}, dY[2] = {0, vg.getStrideY()}, + dZ[2] = {0, vg.getStrideZ()}; + const Real gw[2] = {-Real(1), Real(1)}; + + const Vec3 &pos = p[idx].pos; + const IndexInt fi = static_cast<IndexInt>(pos.x), fj = static_cast<IndexInt>(pos.y), + fk = static_cast<IndexInt>(pos.z); + const IndexInt ci = static_cast<IndexInt>(pos.x - 0.5), + cj = static_cast<IndexInt>(pos.y - 0.5), + ck = static_cast<IndexInt>(pos.z - 0.5); + const Real wfi = clamp(pos.x - fi, Real(0), Real(1)), + wfj = clamp(pos.y - fj, Real(0), Real(1)), + wfk = clamp(pos.z - fk, Real(0), Real(1)); + const Real wci = clamp(Real(pos.x - ci - 0.5), Real(0), Real(1)), + wcj = clamp(Real(pos.y - cj - 0.5), Real(0), Real(1)), + wck = clamp(Real(pos.z - ck - 0.5), Real(0), Real(1)); + // TODO: check index for safety + { // u + const IndexInt gidx = fi * dX[1] + cj * dY[1] + ck * dZ[1]; + const Real wx[2] = {Real(1) - wfi, wfi}; + const Real wy[2] = {Real(1) - wcj, wcj}; + const Real wz[2] = {Real(1) - wck, wck}; + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + for (int k = 0; k < 2; ++k) { + const IndexInt vidx = gidx + dX[i] + dY[j] + dZ[k]; + Real vgx = vg[vidx].x; + vp[idx].x += wx[i] * wy[j] * wz[k] * vgx; + cpx[idx].x += gw[i] * wy[j] * wz[k] * vgx; + cpx[idx].y += wx[i] * gw[j] * wz[k] * vgx; + cpx[idx].z += wx[i] * wy[j] * gw[k] * vgx; + } + } + { // v + const IndexInt gidx = ci * dX[1] + fj * dY[1] + ck * dZ[1]; + const Real wx[2] = {Real(1) - wci, wci}; + const Real wy[2] = {Real(1) - wfj, wfj}; + const Real wz[2] = {Real(1) - wck, wck}; + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + for (int k = 0; k < 2; ++k) { + const IndexInt vidx = gidx + dX[i] + dY[j] + dZ[k]; + Real vgy = vg[vidx].y; + vp[idx].y += wx[i] * wy[j] * wz[k] * vgy; + cpy[idx].x += gw[i] * wy[j] * wz[k] * vgy; + cpy[idx].y += wx[i] * gw[j] * wz[k] * vgy; + cpy[idx].z += wx[i] * wy[j] * gw[k] * vgy; + } + } + if (!vg.is3D()) + return; + { // w + const IndexInt gidx = ci * dX[1] + cj * dY[1] + fk * dZ[1]; + const Real wx[2] = {Real(1) - wci, wci}; + const Real wy[2] = {Real(1) - wcj, wcj}; + const Real wz[2] = {Real(1) - wfk, wfk}; + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + for (int k = 0; k < 2; ++k) { + const IndexInt vidx = gidx + dX[i] + dY[j] + dZ[k]; + Real vgz = vg[vidx].z; + vp[idx].z += wx[i] * wy[j] * wz[k] * vgz; + cpz[idx].x += gw[i] * wy[j] * wz[k] * vgz; + cpz[idx].y += wx[i] * gw[j] * wz[k] * vgz; + cpz[idx].z += wx[i] * wy[j] * gw[k] * vgz; + } + } + } + inline ParticleDataImpl<Vec3> &getArg0() + { + return vp; + } + typedef ParticleDataImpl<Vec3> type0; + inline ParticleDataImpl<Vec3> &getArg1() + { + return cpx; + } + typedef ParticleDataImpl<Vec3> type1; + inline ParticleDataImpl<Vec3> &getArg2() + { + return cpy; + } + typedef ParticleDataImpl<Vec3> type2; + inline ParticleDataImpl<Vec3> &getArg3() + { + return cpz; + } + typedef ParticleDataImpl<Vec3> type3; + inline const BasicParticleSystem &getArg4() + { + return p; + } + typedef BasicParticleSystem type4; + inline const MACGrid &getArg5() + { + return vg; + } + typedef MACGrid type5; + inline const FlagGrid &getArg6() + { + return flags; + } + typedef FlagGrid type6; + inline const ParticleDataImpl<int> *getArg7() + { + return ptype; + } + typedef ParticleDataImpl<int> type7; + inline const int &getArg8() + { + return exclude; + } + typedef int type8; + void runMessage() + { + debMsg("Executing kernel knApicMapLinearMACGridToVec3 ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, vp, cpx, cpy, cpz, p, vg, flags, ptype, exclude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + ParticleDataImpl<Vec3> &vp; + ParticleDataImpl<Vec3> &cpx; + ParticleDataImpl<Vec3> &cpy; + ParticleDataImpl<Vec3> &cpz; + const BasicParticleSystem &p; + const MACGrid &vg; + const FlagGrid &flags; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; + +void apicMapMACGridToParts(ParticleDataImpl<Vec3> &partVel, + ParticleDataImpl<Vec3> &cpx, + ParticleDataImpl<Vec3> &cpy, + ParticleDataImpl<Vec3> &cpz, + const BasicParticleSystem &parts, + const MACGrid &vel, + const FlagGrid &flags, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + knApicMapLinearMACGridToVec3(partVel, cpx, cpy, cpz, parts, vel, flags, ptype, exclude); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "apicMapMACGridToParts", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + ParticleDataImpl<Vec3> &partVel = *_args.getPtr<ParticleDataImpl<Vec3>>( + "partVel", 0, &_lock); + ParticleDataImpl<Vec3> &cpx = *_args.getPtr<ParticleDataImpl<Vec3>>("cpx", 1, &_lock); + ParticleDataImpl<Vec3> &cpy = *_args.getPtr<ParticleDataImpl<Vec3>>("cpy", 2, &_lock); + ParticleDataImpl<Vec3> &cpz = *_args.getPtr<ParticleDataImpl<Vec3>>("cpz", 3, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 4, &_lock); + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 5, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 6, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 7, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 8, 0, &_lock); + _retval = getPyNone(); + apicMapMACGridToParts(partVel, cpx, cpy, cpz, parts, vel, flags, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "apicMapMACGridToParts", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("apicMapMACGridToParts", e.what()); + return 0; + } +} +static const Pb::Register _RP_apicMapMACGridToParts("", "apicMapMACGridToParts", _W_1); +extern "C" { +void PbRegister_apicMapMACGridToParts() +{ + KEEP_UNUSED(_RP_apicMapMACGridToParts); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/extforces.cpp b/extern/mantaflow/preprocessed/plugin/extforces.cpp new file mode 100644 index 00000000000..3e1e5733257 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/extforces.cpp @@ -0,0 +1,1559 @@ + + +// 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 + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Set boundary conditions, gravity + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "grid.h" +#include "commonkernels.h" +#include "particle.h" + +using namespace std; + +namespace Manta { + +//! add constant force between fl/fl and fl/em cells +struct KnApplyForceField : public KernelBase { + KnApplyForceField(const FlagGrid &flags, + MACGrid &vel, + const Grid<Vec3> &force, + const Grid<Real> *include, + bool additive, + bool isMAC) + : KernelBase(&flags, 1), + flags(flags), + vel(vel), + force(force), + include(include), + additive(additive), + isMAC(isMAC) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + MACGrid &vel, + const Grid<Vec3> &force, + const Grid<Real> *include, + bool additive, + bool isMAC) const + { + bool curFluid = flags.isFluid(i, j, k); + bool curEmpty = flags.isEmpty(i, j, k); + if (!curFluid && !curEmpty) + return; + if (include && ((*include)(i, j, k) > 0.)) + return; + + Real forceX = (isMAC) ? force(i, j, k).x : 0.5 * (force(i - 1, j, k).x + force(i, j, k).x); + Real forceY = (isMAC) ? force(i, j, k).y : 0.5 * (force(i, j - 1, k).y + force(i, j, k).y); + + Real forceZ = 0.; + if (vel.is3D()) + forceZ = (isMAC) ? force(i, j, k).z : 0.5 * (force(i, j, k - 1).z + force(i, j, k).z); + + if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k))) + vel(i, j, k).x = (additive) ? vel(i, j, k).x + forceX : forceX; + if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k))) + vel(i, j, k).y = (additive) ? vel(i, j, k).y + forceY : forceY; + if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1)))) + vel(i, j, k).z = (additive) ? vel(i, j, k).z + forceZ : forceZ; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline const Grid<Vec3> &getArg2() + { + return force; + } + typedef Grid<Vec3> type2; + inline const Grid<Real> *getArg3() + { + return include; + } + typedef Grid<Real> type3; + inline bool &getArg4() + { + return additive; + } + typedef bool type4; + inline bool &getArg5() + { + return isMAC; + } + typedef bool type5; + void runMessage() + { + debMsg("Executing kernel KnApplyForceField ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, force, include, additive, isMAC); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, force, include, additive, isMAC); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + MACGrid &vel; + const Grid<Vec3> &force; + const Grid<Real> *include; + bool additive; + bool isMAC; +}; + +//! add constant force between fl/fl and fl/em cells +struct KnApplyForce : public KernelBase { + KnApplyForce( + const FlagGrid &flags, MACGrid &vel, Vec3 force, const Grid<Real> *exclude, bool additive) + : KernelBase(&flags, 1), + flags(flags), + vel(vel), + force(force), + exclude(exclude), + additive(additive) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + MACGrid &vel, + Vec3 force, + const Grid<Real> *exclude, + bool additive) const + { + bool curFluid = flags.isFluid(i, j, k); + bool curEmpty = flags.isEmpty(i, j, k); + if (!curFluid && !curEmpty) + return; + if (exclude && ((*exclude)(i, j, k) < 0.)) + return; + + if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k))) + vel(i, j, k).x = (additive) ? vel(i, j, k).x + force.x : force.x; + if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k))) + vel(i, j, k).y = (additive) ? vel(i, j, k).y + force.y : force.y; + if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1)))) + vel(i, j, k).z = (additive) ? vel(i, j, k).z + force.z : force.z; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline Vec3 &getArg2() + { + return force; + } + typedef Vec3 type2; + inline const Grid<Real> *getArg3() + { + return exclude; + } + typedef Grid<Real> type3; + inline bool &getArg4() + { + return additive; + } + typedef bool type4; + void runMessage() + { + debMsg("Executing kernel KnApplyForce ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, force, exclude, additive); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, force, exclude, additive); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + MACGrid &vel; + Vec3 force; + const Grid<Real> *exclude; + bool additive; +}; + +//! add gravity forces to all fluid cells, automatically adapts to different grid sizes +void addGravity(const FlagGrid &flags, + MACGrid &vel, + Vec3 gravity, + const Grid<Real> *exclude = NULL) +{ + Vec3 f = gravity * flags.getParent()->getDt() / flags.getDx(); + KnApplyForce(flags, vel, f, exclude, true); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "addGravity", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + Vec3 gravity = _args.get<Vec3>("gravity", 2, &_lock); + const Grid<Real> *exclude = _args.getPtrOpt<Grid<Real>>("exclude", 3, NULL, &_lock); + _retval = getPyNone(); + addGravity(flags, vel, gravity, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "addGravity", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("addGravity", e.what()); + return 0; + } +} +static const Pb::Register _RP_addGravity("", "addGravity", _W_0); +extern "C" { +void PbRegister_addGravity() +{ + KEEP_UNUSED(_RP_addGravity); +} +} + +//! add gravity forces to all fluid cells , but dont account for changing cell size +void addGravityNoScale(const FlagGrid &flags, + MACGrid &vel, + const Vec3 &gravity, + const Grid<Real> *exclude = NULL) +{ + const Vec3 f = gravity * flags.getParent()->getDt(); + KnApplyForce(flags, vel, f, exclude, true); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "addGravityNoScale", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const Vec3 &gravity = _args.get<Vec3>("gravity", 2, &_lock); + const Grid<Real> *exclude = _args.getPtrOpt<Grid<Real>>("exclude", 3, NULL, &_lock); + _retval = getPyNone(); + addGravityNoScale(flags, vel, gravity, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "addGravityNoScale", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("addGravityNoScale", e.what()); + return 0; + } +} +static const Pb::Register _RP_addGravityNoScale("", "addGravityNoScale", _W_1); +extern "C" { +void PbRegister_addGravityNoScale() +{ + KEEP_UNUSED(_RP_addGravityNoScale); +} +} + +//! kernel to add Buoyancy force +struct KnAddBuoyancy : public KernelBase { + KnAddBuoyancy(const FlagGrid &flags, const Grid<Real> &factor, MACGrid &vel, Vec3 strength) + : KernelBase(&flags, 1), flags(flags), factor(factor), vel(vel), strength(strength) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const Grid<Real> &factor, + MACGrid &vel, + Vec3 strength) const + { + if (!flags.isFluid(i, j, k)) + return; + if (flags.isFluid(i - 1, j, k)) + vel(i, j, k).x += (0.5 * strength.x) * (factor(i, j, k) + factor(i - 1, j, k)); + if (flags.isFluid(i, j - 1, k)) + vel(i, j, k).y += (0.5 * strength.y) * (factor(i, j, k) + factor(i, j - 1, k)); + if (vel.is3D() && flags.isFluid(i, j, k - 1)) + vel(i, j, k).z += (0.5 * strength.z) * (factor(i, j, k) + factor(i, j, k - 1)); + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const Grid<Real> &getArg1() + { + return factor; + } + typedef Grid<Real> type1; + inline MACGrid &getArg2() + { + return vel; + } + typedef MACGrid type2; + inline Vec3 &getArg3() + { + return strength; + } + typedef Vec3 type3; + void runMessage() + { + debMsg("Executing kernel KnAddBuoyancy ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, factor, vel, strength); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, factor, vel, strength); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + const Grid<Real> &factor; + MACGrid &vel; + Vec3 strength; +}; + +//! add Buoyancy force based on fctor (e.g. smoke density) +void addBuoyancy(const FlagGrid &flags, + const Grid<Real> &density, + MACGrid &vel, + Vec3 gravity, + Real coefficient = 1.) +{ + Vec3 f = -gravity * flags.getParent()->getDt() / flags.getParent()->getDx() * coefficient; + KnAddBuoyancy(flags, density, vel, f); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "addBuoyancy", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + const Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 2, &_lock); + Vec3 gravity = _args.get<Vec3>("gravity", 3, &_lock); + Real coefficient = _args.getOpt<Real>("coefficient", 4, 1., &_lock); + _retval = getPyNone(); + addBuoyancy(flags, density, vel, gravity, coefficient); + _args.check(); + } + pbFinalizePlugin(parent, "addBuoyancy", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("addBuoyancy", e.what()); + return 0; + } +} +static const Pb::Register _RP_addBuoyancy("", "addBuoyancy", _W_2); +extern "C" { +void PbRegister_addBuoyancy() +{ + KEEP_UNUSED(_RP_addBuoyancy); +} +} + +// inflow / outflow boundaries + +//! helper to parse openbounds string [xXyYzZ] , convert to vec3 +inline void convertDescToVec(const string &desc, Vector3D<bool> &lo, Vector3D<bool> &up) +{ + for (size_t i = 0; i < desc.size(); i++) { + if (desc[i] == 'x') + lo.x = true; + else if (desc[i] == 'y') + lo.y = true; + else if (desc[i] == 'z') + lo.z = true; + else if (desc[i] == 'X') + up.x = true; + else if (desc[i] == 'Y') + up.y = true; + else if (desc[i] == 'Z') + up.z = true; + else + errMsg("invalid character in boundary description string. Only [xyzXYZ] allowed."); + } +} + +//! add empty and outflow flag to cells of open boundaries +void setOpenBound(FlagGrid &flags, + int bWidth, + string openBound = "", + int type = FlagGrid::TypeOutflow | FlagGrid::TypeEmpty) +{ + if (openBound == "") + return; + Vector3D<bool> lo, up; + convertDescToVec(openBound, lo, up); + + FOR_IJK(flags) + { + bool loX = lo.x && i <= bWidth; // a cell which belongs to the lower x open bound + bool loY = lo.y && j <= bWidth; + bool upX = up.x && i >= flags.getSizeX() - bWidth - + 1; // a cell which belongs to the upper x open bound + bool upY = up.y && j >= flags.getSizeY() - bWidth - 1; + bool innerI = i > bWidth && + i < flags.getSizeX() - bWidth - + 1; // a cell which does not belong to the lower or upper x bound + bool innerJ = j > bWidth && j < flags.getSizeY() - bWidth - 1; + + // when setting boundaries to open: don't set shared part of wall to empty if neighboring wall + // is not open + if ((!flags.is3D()) && (loX || upX || loY || upY)) { + if ((loX || upX || innerI) && (loY || upY || innerJ) && flags.isObstacle(i, j, k)) + flags(i, j, k) = type; + } + else { + bool loZ = lo.z && k <= bWidth; // a cell which belongs to the lower z open bound + bool upZ = up.z && k >= flags.getSizeZ() - bWidth - + 1; // a cell which belongs to the upper z open bound + bool innerK = k > bWidth && + k < flags.getSizeZ() - bWidth - + 1; // a cell which does not belong to the lower or upper z bound + if (loX || upX || loY || upY || loZ || upZ) { + if ((loX || upX || innerI) && (loY || upY || innerJ) && (loZ || upZ || innerK) && + flags.isObstacle(i, j, k)) + flags(i, j, k) = type; + } + } + } +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setOpenBound", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + int bWidth = _args.get<int>("bWidth", 1, &_lock); + string openBound = _args.getOpt<string>("openBound", 2, "", &_lock); + int type = _args.getOpt<int>("type", 3, FlagGrid::TypeOutflow | FlagGrid::TypeEmpty, &_lock); + _retval = getPyNone(); + setOpenBound(flags, bWidth, openBound, type); + _args.check(); + } + pbFinalizePlugin(parent, "setOpenBound", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setOpenBound", e.what()); + return 0; + } +} +static const Pb::Register _RP_setOpenBound("", "setOpenBound", _W_3); +extern "C" { +void PbRegister_setOpenBound() +{ + KEEP_UNUSED(_RP_setOpenBound); +} +} + +//! delete fluid and ensure empty flag in outflow cells, delete particles and density and set phi +//! to 0.5 +void resetOutflow(FlagGrid &flags, + Grid<Real> *phi = 0, + BasicParticleSystem *parts = 0, + Grid<Real> *real = 0, + Grid<int> *index = 0, + ParticleIndexSystem *indexSys = 0) +{ + // check if phi and parts -> pindex and gpi already created -> access particles from cell index, + // avoid extra looping over particles + if (parts && (!index || !indexSys)) { + if (phi) + debMsg( + "resetOpenBound for phi and particles, but missing index and indexSys for enhanced " + "particle access!", + 1); + for (int idx = 0; idx < (int)parts->size(); idx++) + if (parts->isActive(idx) && flags.isInBounds(parts->getPos(idx)) && + flags.isOutflow(parts->getPos(idx))) + parts->kill(idx); + } + FOR_IJK(flags) + { + if (flags.isOutflow(i, j, k)) { + flags(i, j, k) = (flags(i, j, k) | FlagGrid::TypeEmpty) & + ~FlagGrid::TypeFluid; // make sure there is not fluid flag set and to reset + // the empty flag + // the particles in a cell i,j,k are particles[index(i,j,k)] to particles[index(i+1,j,k)-1] + if (parts && index && indexSys) { + int isysIdxS = index->index(i, j, k); + int pStart = (*index)(isysIdxS), pEnd = 0; + if (flags.isInBounds(isysIdxS + 1)) + pEnd = (*index)(isysIdxS + 1); + else + pEnd = indexSys->size(); + // now loop over particles in cell + for (int p = pStart; p < pEnd; ++p) { + int psrc = (*indexSys)[p].sourceIndex; + if (parts->isActive(psrc) && flags.isInBounds(parts->getPos(psrc))) + parts->kill(psrc); + } + } + if (phi) + (*phi)(i, j, k) = 0.5; + if (real) + (*real)(i, j, k) = 0; + } + } + if (parts) + parts->doCompress(); +} +static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "resetOutflow", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> *phi = _args.getPtrOpt<Grid<Real>>("phi", 1, 0, &_lock); + BasicParticleSystem *parts = _args.getPtrOpt<BasicParticleSystem>("parts", 2, 0, &_lock); + Grid<Real> *real = _args.getPtrOpt<Grid<Real>>("real", 3, 0, &_lock); + Grid<int> *index = _args.getPtrOpt<Grid<int>>("index", 4, 0, &_lock); + ParticleIndexSystem *indexSys = _args.getPtrOpt<ParticleIndexSystem>( + "indexSys", 5, 0, &_lock); + _retval = getPyNone(); + resetOutflow(flags, phi, parts, real, index, indexSys); + _args.check(); + } + pbFinalizePlugin(parent, "resetOutflow", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("resetOutflow", e.what()); + return 0; + } +} +static const Pb::Register _RP_resetOutflow("", "resetOutflow", _W_4); +extern "C" { +void PbRegister_resetOutflow() +{ + KEEP_UNUSED(_RP_resetOutflow); +} +} + +//! enforce a constant inflow/outflow at the grid boundaries +struct KnSetInflow : public KernelBase { + KnSetInflow(MACGrid &vel, int dim, int p0, const Vec3 &val) + : KernelBase(&vel, 0), vel(vel), dim(dim), p0(p0), val(val) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, MACGrid &vel, int dim, int p0, const Vec3 &val) const + { + Vec3i p(i, j, k); + if (p[dim] == p0 || p[dim] == p0 + 1) + vel(i, j, k) = val; + } + inline MACGrid &getArg0() + { + return vel; + } + typedef MACGrid type0; + inline int &getArg1() + { + return dim; + } + typedef int type1; + inline int &getArg2() + { + return p0; + } + typedef int type2; + inline const Vec3 &getArg3() + { + return val; + } + typedef Vec3 type3; + void runMessage() + { + debMsg("Executing kernel KnSetInflow ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, vel, dim, p0, val); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, vel, dim, p0, val); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + MACGrid &vel; + int dim; + int p0; + const Vec3 &val; +}; + +//! enforce a constant inflow/outflow at the grid boundaries +void setInflowBcs(MACGrid &vel, string dir, Vec3 value) +{ + for (size_t i = 0; i < dir.size(); i++) { + if (dir[i] >= 'x' && dir[i] <= 'z') { + int dim = dir[i] - 'x'; + KnSetInflow(vel, dim, 0, value); + } + else if (dir[i] >= 'X' && dir[i] <= 'Z') { + int dim = dir[i] - 'X'; + KnSetInflow(vel, dim, vel.getSize()[dim] - 1, value); + } + else + errMsg("invalid character in direction string. Only [xyzXYZ] allowed."); + } +} +static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setInflowBcs", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + string dir = _args.get<string>("dir", 1, &_lock); + Vec3 value = _args.get<Vec3>("value", 2, &_lock); + _retval = getPyNone(); + setInflowBcs(vel, dir, value); + _args.check(); + } + pbFinalizePlugin(parent, "setInflowBcs", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setInflowBcs", e.what()); + return 0; + } +} +static const Pb::Register _RP_setInflowBcs("", "setInflowBcs", _W_5); +extern "C" { +void PbRegister_setInflowBcs() +{ + KEEP_UNUSED(_RP_setInflowBcs); +} +} + +// set obstacle boundary conditions + +//! set no-stick wall boundary condition between ob/fl and ob/ob cells +struct KnSetWallBcs : public KernelBase { + KnSetWallBcs(const FlagGrid &flags, MACGrid &vel, const MACGrid *obvel) + : KernelBase(&flags, 0), flags(flags), vel(vel), obvel(obvel) + { + runMessage(); + run(); + } + inline void op( + int i, int j, int k, const FlagGrid &flags, MACGrid &vel, const MACGrid *obvel) const + { + + bool curFluid = flags.isFluid(i, j, k); + bool curObs = flags.isObstacle(i, j, k); + Vec3 bcsVel(0., 0., 0.); + if (!curFluid && !curObs) + return; + + if (obvel) { + bcsVel.x = (*obvel)(i, j, k).x; + bcsVel.y = (*obvel)(i, j, k).y; + if ((*obvel).is3D()) + bcsVel.z = (*obvel)(i, j, k).z; + } + + // we use i>0 instead of bnd=1 to check outer wall + if (i > 0 && flags.isObstacle(i - 1, j, k)) + vel(i, j, k).x = bcsVel.x; + if (i > 0 && curObs && flags.isFluid(i - 1, j, k)) + vel(i, j, k).x = bcsVel.x; + if (j > 0 && flags.isObstacle(i, j - 1, k)) + vel(i, j, k).y = bcsVel.y; + if (j > 0 && curObs && flags.isFluid(i, j - 1, k)) + vel(i, j, k).y = bcsVel.y; + + if (!vel.is3D()) { + vel(i, j, k).z = 0; + } + else { + if (k > 0 && flags.isObstacle(i, j, k - 1)) + vel(i, j, k).z = bcsVel.z; + if (k > 0 && curObs && flags.isFluid(i, j, k - 1)) + vel(i, j, k).z = bcsVel.z; + } + + if (curFluid) { + if ((i > 0 && flags.isStick(i - 1, j, k)) || + (i < flags.getSizeX() - 1 && flags.isStick(i + 1, j, k))) + vel(i, j, k).y = vel(i, j, k).z = 0; + if ((j > 0 && flags.isStick(i, j - 1, k)) || + (j < flags.getSizeY() - 1 && flags.isStick(i, j + 1, k))) + vel(i, j, k).x = vel(i, j, k).z = 0; + if (vel.is3D() && ((k > 0 && flags.isStick(i, j, k - 1)) || + (k < flags.getSizeZ() - 1 && flags.isStick(i, j, k + 1)))) + vel(i, j, k).x = vel(i, j, k).y = 0; + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline const MACGrid *getArg2() + { + return obvel; + } + typedef MACGrid type2; + void runMessage() + { + debMsg("Executing kernel KnSetWallBcs ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, obvel); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, obvel); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + MACGrid &vel; + const MACGrid *obvel; +}; + +//! set wall BCs for fill fraction mode, note - only needs obstacle SDF + +struct KnSetWallBcsFrac : public KernelBase { + KnSetWallBcsFrac(const FlagGrid &flags, + const MACGrid &vel, + MACGrid &velTarget, + const MACGrid *obvel, + const Grid<Real> *phiObs, + const int &boundaryWidth = 0) + : KernelBase(&flags, 0), + flags(flags), + vel(vel), + velTarget(velTarget), + obvel(obvel), + phiObs(phiObs), + boundaryWidth(boundaryWidth) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &vel, + MACGrid &velTarget, + const MACGrid *obvel, + const Grid<Real> *phiObs, + const int &boundaryWidth = 0) const + { + bool curFluid = flags.isFluid(i, j, k); + bool curObs = flags.isObstacle(i, j, k); + velTarget(i, j, k) = vel(i, j, k); + if (!curFluid && !curObs) + return; + + // zero normal component in all obstacle regions + if (flags.isInBounds(Vec3i(i, j, k), 1)) { + + if (curObs | flags.isObstacle(i - 1, j, k)) { + Vec3 dphi(0., 0., 0.); + const Real tmp1 = (phiObs->get(i, j, k) + phiObs->get(i - 1, j, k)) * .5; + Real tmp2 = (phiObs->get(i, j + 1, k) + phiObs->get(i - 1, j + 1, k)) * .5; + Real phi1 = (tmp1 + tmp2) * .5; + tmp2 = (phiObs->get(i, j - 1, k) + phiObs->get(i - 1, j - 1, k)) * .5; + Real phi2 = (tmp1 + tmp2) * .5; + + dphi.x = phiObs->get(i, j, k) - phiObs->get(i - 1, j, k); + dphi.y = phi1 - phi2; + + if (phiObs->is3D()) { + tmp2 = (phiObs->get(i, j, k + 1) + phiObs->get(i - 1, j, k + 1)) * .5; + phi1 = (tmp1 + tmp2) * .5; + tmp2 = (phiObs->get(i, j, k - 1) + phiObs->get(i - 1, j, k - 1)) * .5; + phi2 = (tmp1 + tmp2) * .5; + dphi.z = phi1 - phi2; + } + + normalize(dphi); + Vec3 velMAC = vel.getAtMACX(i, j, k); + velTarget(i, j, k).x = velMAC.x - dot(dphi, velMAC) * dphi.x; + } + + if (curObs | flags.isObstacle(i, j - 1, k)) { + Vec3 dphi(0., 0., 0.); + const Real tmp1 = (phiObs->get(i, j, k) + phiObs->get(i, j - 1, k)) * .5; + Real tmp2 = (phiObs->get(i + 1, j, k) + phiObs->get(i + 1, j - 1, k)) * .5; + Real phi1 = (tmp1 + tmp2) * .5; + tmp2 = (phiObs->get(i - 1, j, k) + phiObs->get(i - 1, j - 1, k)) * .5; + Real phi2 = (tmp1 + tmp2) * .5; + + dphi.x = phi1 - phi2; + dphi.y = phiObs->get(i, j, k) - phiObs->get(i, j - 1, k); + if (phiObs->is3D()) { + tmp2 = (phiObs->get(i, j, k + 1) + phiObs->get(i, j - 1, k + 1)) * .5; + phi1 = (tmp1 + tmp2) * .5; + tmp2 = (phiObs->get(i, j, k - 1) + phiObs->get(i, j - 1, k - 1)) * .5; + phi2 = (tmp1 + tmp2) * .5; + dphi.z = phi1 - phi2; + } + + normalize(dphi); + Vec3 velMAC = vel.getAtMACY(i, j, k); + velTarget(i, j, k).y = velMAC.y - dot(dphi, velMAC) * dphi.y; + } + + if (phiObs->is3D() && (curObs | flags.isObstacle(i, j, k - 1))) { + Vec3 dphi(0., 0., 0.); + const Real tmp1 = (phiObs->get(i, j, k) + phiObs->get(i, j, k - 1)) * .5; + + Real tmp2; + tmp2 = (phiObs->get(i + 1, j, k) + phiObs->get(i + 1, j, k - 1)) * .5; + Real phi1 = (tmp1 + tmp2) * .5; + tmp2 = (phiObs->get(i - 1, j, k) + phiObs->get(i - 1, j, k - 1)) * .5; + Real phi2 = (tmp1 + tmp2) * .5; + dphi.x = phi1 - phi2; + + tmp2 = (phiObs->get(i, j + 1, k) + phiObs->get(i, j + 1, k - 1)) * .5; + phi1 = (tmp1 + tmp2) * .5; + tmp2 = (phiObs->get(i, j - 1, k) + phiObs->get(i, j - 1, k - 1)) * .5; + phi2 = (tmp1 + tmp2) * .5; + dphi.y = phi1 - phi2; + + dphi.z = phiObs->get(i, j, k) - phiObs->get(i, j, k - 1); + + normalize(dphi); + Vec3 velMAC = vel.getAtMACZ(i, j, k); + velTarget(i, j, k).z = velMAC.z - dot(dphi, velMAC) * dphi.z; + } + } // not at boundary + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline MACGrid &getArg2() + { + return velTarget; + } + typedef MACGrid type2; + inline const MACGrid *getArg3() + { + return obvel; + } + typedef MACGrid type3; + inline const Grid<Real> *getArg4() + { + return phiObs; + } + typedef Grid<Real> type4; + inline const int &getArg5() + { + return boundaryWidth; + } + typedef int type5; + void runMessage() + { + debMsg("Executing kernel KnSetWallBcsFrac ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, velTarget, obvel, phiObs, boundaryWidth); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, velTarget, obvel, phiObs, boundaryWidth); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &vel; + MACGrid &velTarget; + const MACGrid *obvel; + const Grid<Real> *phiObs; + const int &boundaryWidth; +}; + +//! set zero normal velocity boundary condition on walls +// (optionally with second order accuracy using the obstacle SDF , fractions grid currentlyl not +// needed) +void setWallBcs(const FlagGrid &flags, + MACGrid &vel, + const MACGrid *obvel = 0, + const MACGrid *fractions = 0, + const Grid<Real> *phiObs = 0, + int boundaryWidth = 0) +{ + if (!phiObs || !fractions) { + KnSetWallBcs(flags, vel, obvel); + } + else { + MACGrid tmpvel(vel.getParent()); + KnSetWallBcsFrac(flags, vel, tmpvel, obvel, phiObs, boundaryWidth); + vel.swap(tmpvel); + } +} +static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setWallBcs", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const MACGrid *obvel = _args.getPtrOpt<MACGrid>("obvel", 2, 0, &_lock); + const MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 3, 0, &_lock); + const Grid<Real> *phiObs = _args.getPtrOpt<Grid<Real>>("phiObs", 4, 0, &_lock); + int boundaryWidth = _args.getOpt<int>("boundaryWidth", 5, 0, &_lock); + _retval = getPyNone(); + setWallBcs(flags, vel, obvel, fractions, phiObs, boundaryWidth); + _args.check(); + } + pbFinalizePlugin(parent, "setWallBcs", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setWallBcs", e.what()); + return 0; + } +} +static const Pb::Register _RP_setWallBcs("", "setWallBcs", _W_6); +extern "C" { +void PbRegister_setWallBcs() +{ + KEEP_UNUSED(_RP_setWallBcs); +} +} + +//! add Forces between fl/fl and fl/em cells (interpolate cell centered forces to MAC grid) +struct KnAddForceIfLower : public KernelBase { + KnAddForceIfLower(const FlagGrid &flags, MACGrid &vel, const Grid<Vec3> &force) + : KernelBase(&flags, 1), flags(flags), vel(vel), force(force) + { + runMessage(); + run(); + } + inline void op( + int i, int j, int k, const FlagGrid &flags, MACGrid &vel, const Grid<Vec3> &force) const + { + bool curFluid = flags.isFluid(i, j, k); + bool curEmpty = flags.isEmpty(i, j, k); + if (!curFluid && !curEmpty) + return; + + if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k))) { + Real forceMACX = 0.5 * (force(i - 1, j, k).x + force(i, j, k).x); + Real min = std::min(vel(i, j, k).x, forceMACX); + Real max = std::max(vel(i, j, k).x, forceMACX); + Real sum = vel(i, j, k).x + forceMACX; + vel(i, j, k).x = (forceMACX > 0) ? std::min(sum, max) : std::max(sum, min); + } + if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k))) { + Real forceMACY = 0.5 * (force(i, j - 1, k).y + force(i, j, k).y); + Real min = std::min(vel(i, j, k).y, forceMACY); + Real max = std::max(vel(i, j, k).y, forceMACY); + Real sum = vel(i, j, k).y + forceMACY; + vel(i, j, k).y = (forceMACY > 0) ? std::min(sum, max) : std::max(sum, min); + } + if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1)))) { + Real forceMACZ = 0.5 * (force(i, j, k - 1).z + force(i, j, k).z); + Real min = std::min(vel(i, j, k).z, forceMACZ); + Real max = std::max(vel(i, j, k).z, forceMACZ); + Real sum = vel(i, j, k).z + forceMACZ; + vel(i, j, k).z = (forceMACZ > 0) ? std::min(sum, max) : std::max(sum, min); + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline const Grid<Vec3> &getArg2() + { + return force; + } + typedef Grid<Vec3> type2; + void runMessage() + { + debMsg("Executing kernel KnAddForceIfLower ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, force); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, force); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + MACGrid &vel; + const Grid<Vec3> &force; +}; + +// Initial velocity for smoke +void setInitialVelocity(const FlagGrid &flags, MACGrid &vel, const Grid<Vec3> &invel) +{ + KnAddForceIfLower(flags, vel, invel); +} +static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setInitialVelocity", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const Grid<Vec3> &invel = *_args.getPtr<Grid<Vec3>>("invel", 2, &_lock); + _retval = getPyNone(); + setInitialVelocity(flags, vel, invel); + _args.check(); + } + pbFinalizePlugin(parent, "setInitialVelocity", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setInitialVelocity", e.what()); + return 0; + } +} +static const Pb::Register _RP_setInitialVelocity("", "setInitialVelocity", _W_7); +extern "C" { +void PbRegister_setInitialVelocity() +{ + KEEP_UNUSED(_RP_setInitialVelocity); +} +} + +//! Kernel: gradient norm operator +struct KnConfForce : public KernelBase { + KnConfForce(Grid<Vec3> &force, + const Grid<Real> &grid, + const Grid<Vec3> &curl, + Real str, + const Grid<Real> *strGrid) + : KernelBase(&force, 1), force(force), grid(grid), curl(curl), str(str), strGrid(strGrid) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<Vec3> &force, + const Grid<Real> &grid, + const Grid<Vec3> &curl, + Real str, + const Grid<Real> *strGrid) const + { + Vec3 grad = 0.5 * Vec3(grid(i + 1, j, k) - grid(i - 1, j, k), + grid(i, j + 1, k) - grid(i, j - 1, k), + 0.); + if (grid.is3D()) + grad[2] = 0.5 * (grid(i, j, k + 1) - grid(i, j, k - 1)); + normalize(grad); + if (strGrid) + str += (*strGrid)(i, j, k); + force(i, j, k) = str * cross(grad, curl(i, j, k)); + } + inline Grid<Vec3> &getArg0() + { + return force; + } + typedef Grid<Vec3> type0; + inline const Grid<Real> &getArg1() + { + return grid; + } + typedef Grid<Real> type1; + inline const Grid<Vec3> &getArg2() + { + return curl; + } + typedef Grid<Vec3> type2; + inline Real &getArg3() + { + return str; + } + typedef Real type3; + inline const Grid<Real> *getArg4() + { + return strGrid; + } + typedef Grid<Real> type4; + void runMessage() + { + debMsg("Executing kernel KnConfForce ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, force, grid, curl, str, strGrid); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, force, grid, curl, str, strGrid); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + Grid<Vec3> &force; + const Grid<Real> &grid; + const Grid<Vec3> &curl; + Real str; + const Grid<Real> *strGrid; +}; + +void vorticityConfinement(MACGrid &vel, + const FlagGrid &flags, + Real strengthGlobal = 0, + const Grid<Real> *strengthCell = NULL) +{ + Grid<Vec3> velCenter(flags.getParent()), curl(flags.getParent()), force(flags.getParent()); + Grid<Real> norm(flags.getParent()); + + GetCentered(velCenter, vel); + CurlOp(velCenter, curl); + GridNorm(norm, curl); + KnConfForce(force, norm, curl, strengthGlobal, strengthCell); + KnApplyForceField(flags, vel, force, NULL, true, false); +} +static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "vorticityConfinement", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + Real strengthGlobal = _args.getOpt<Real>("strengthGlobal", 2, 0, &_lock); + const Grid<Real> *strengthCell = _args.getPtrOpt<Grid<Real>>( + "strengthCell", 3, NULL, &_lock); + _retval = getPyNone(); + vorticityConfinement(vel, flags, strengthGlobal, strengthCell); + _args.check(); + } + pbFinalizePlugin(parent, "vorticityConfinement", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("vorticityConfinement", e.what()); + return 0; + } +} +static const Pb::Register _RP_vorticityConfinement("", "vorticityConfinement", _W_8); +extern "C" { +void PbRegister_vorticityConfinement() +{ + KEEP_UNUSED(_RP_vorticityConfinement); +} +} + +void addForceField(const FlagGrid &flags, + MACGrid &vel, + const Grid<Vec3> &force, + const Grid<Real> *region = NULL, + bool isMAC = false) +{ + KnApplyForceField(flags, vel, force, region, true, isMAC); +} +static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "addForceField", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const Grid<Vec3> &force = *_args.getPtr<Grid<Vec3>>("force", 2, &_lock); + const Grid<Real> *region = _args.getPtrOpt<Grid<Real>>("region", 3, NULL, &_lock); + bool isMAC = _args.getOpt<bool>("isMAC", 4, false, &_lock); + _retval = getPyNone(); + addForceField(flags, vel, force, region, isMAC); + _args.check(); + } + pbFinalizePlugin(parent, "addForceField", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("addForceField", e.what()); + return 0; + } +} +static const Pb::Register _RP_addForceField("", "addForceField", _W_9); +extern "C" { +void PbRegister_addForceField() +{ + KEEP_UNUSED(_RP_addForceField); +} +} + +void setForceField(const FlagGrid &flags, + MACGrid &vel, + const Grid<Vec3> &force, + const Grid<Real> *region = NULL, + bool isMAC = false) +{ + KnApplyForceField(flags, vel, force, region, false, isMAC); +} +static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setForceField", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const Grid<Vec3> &force = *_args.getPtr<Grid<Vec3>>("force", 2, &_lock); + const Grid<Real> *region = _args.getPtrOpt<Grid<Real>>("region", 3, NULL, &_lock); + bool isMAC = _args.getOpt<bool>("isMAC", 4, false, &_lock); + _retval = getPyNone(); + setForceField(flags, vel, force, region, isMAC); + _args.check(); + } + pbFinalizePlugin(parent, "setForceField", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setForceField", e.what()); + return 0; + } +} +static const Pb::Register _RP_setForceField("", "setForceField", _W_10); +extern "C" { +void PbRegister_setForceField() +{ + KEEP_UNUSED(_RP_setForceField); +} +} + +void dissolveSmoke(const FlagGrid &flags, + Grid<Real> &density, + Grid<Real> *heat = NULL, + Grid<Real> *red = NULL, + Grid<Real> *green = NULL, + Grid<Real> *blue = NULL, + int speed = 5, + bool logFalloff = true) +{ + float dydx = 1.0f / (float)speed; // max density/speed = dydx + float fac = 1.0f - dydx; + + FOR_IJK_BND(density, 0) + { + bool curFluid = flags.isFluid(i, j, k); + if (!curFluid) + continue; + + if (logFalloff) { + density(i, j, k) *= fac; + if (heat) { + (*heat)(i, j, k) *= fac; + } + if (red) { + (*red)(i, j, k) *= fac; + (*green)(i, j, k) *= fac; + (*blue)(i, j, k) *= fac; + } + } + else { // linear falloff + float d = density(i, j, k); + density(i, j, k) -= dydx; + if (density(i, j, k) < 0.0f) + density(i, j, k) = 0.0f; + if (heat) { + if (fabs((*heat)(i, j, k)) < dydx) + (*heat)(i, j, k) = 0.0f; + else if ((*heat)(i, j, k) > 0.0f) + (*heat)(i, j, k) -= dydx; + else if ((*heat)(i, j, k) < 0.0f) + (*heat)(i, j, k) += dydx; + } + if (red && notZero(d)) { + (*red)(i, j, k) *= (density(i, j, k) / d); + (*green)(i, j, k) *= (density(i, j, k) / d); + (*blue)(i, j, k) *= (density(i, j, k) / d); + } + } + } +} +static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "dissolveSmoke", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + Grid<Real> *heat = _args.getPtrOpt<Grid<Real>>("heat", 2, NULL, &_lock); + Grid<Real> *red = _args.getPtrOpt<Grid<Real>>("red", 3, NULL, &_lock); + Grid<Real> *green = _args.getPtrOpt<Grid<Real>>("green", 4, NULL, &_lock); + Grid<Real> *blue = _args.getPtrOpt<Grid<Real>>("blue", 5, NULL, &_lock); + int speed = _args.getOpt<int>("speed", 6, 5, &_lock); + bool logFalloff = _args.getOpt<bool>("logFalloff", 7, true, &_lock); + _retval = getPyNone(); + dissolveSmoke(flags, density, heat, red, green, blue, speed, logFalloff); + _args.check(); + } + pbFinalizePlugin(parent, "dissolveSmoke", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("dissolveSmoke", e.what()); + return 0; + } +} +static const Pb::Register _RP_dissolveSmoke("", "dissolveSmoke", _W_11); +extern "C" { +void PbRegister_dissolveSmoke() +{ + KEEP_UNUSED(_RP_dissolveSmoke); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/fire.cpp b/extern/mantaflow/preprocessed/plugin/fire.cpp new file mode 100644 index 00000000000..9047d4bf8a1 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/fire.cpp @@ -0,0 +1,435 @@ + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2016 Sebastian Barschkis, 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 + * + * Fire modeling plugin + * + ******************************************************************************/ + +#include "general.h" +#include "grid.h" +#include "vectorbase.h" + +using namespace std; + +namespace Manta { + +struct KnProcessBurn : public KernelBase { + KnProcessBurn(Grid<Real> &fuel, + Grid<Real> &density, + Grid<Real> &react, + Grid<Real> *red, + Grid<Real> *green, + Grid<Real> *blue, + Grid<Real> *heat, + Real burningRate, + Real flameSmoke, + Real ignitionTemp, + Real maxTemp, + Real dt, + Vec3 flameSmokeColor) + : KernelBase(&fuel, 1), + fuel(fuel), + density(density), + react(react), + red(red), + green(green), + blue(blue), + heat(heat), + burningRate(burningRate), + flameSmoke(flameSmoke), + ignitionTemp(ignitionTemp), + maxTemp(maxTemp), + dt(dt), + flameSmokeColor(flameSmokeColor) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<Real> &fuel, + Grid<Real> &density, + Grid<Real> &react, + Grid<Real> *red, + Grid<Real> *green, + Grid<Real> *blue, + Grid<Real> *heat, + Real burningRate, + Real flameSmoke, + Real ignitionTemp, + Real maxTemp, + Real dt, + Vec3 flameSmokeColor) const + { + // Save initial values + Real origFuel = fuel(i, j, k); + Real origSmoke = density(i, j, k); + Real smokeEmit = 0.0f; + Real flame = 0.0f; + + // Process fuel + fuel(i, j, k) -= burningRate * dt; + if (fuel(i, j, k) < 0.0f) + fuel(i, j, k) = 0.0f; + + // Process reaction coordinate + if (origFuel > VECTOR_EPSILON) { + react(i, j, k) *= fuel(i, j, k) / origFuel; + flame = pow(react(i, j, k), 0.5f); + } + else { + react(i, j, k) = 0.0f; + } + + // Set fluid temperature based on fuel burn rate and "flameSmoke" factor + smokeEmit = (origFuel < 1.0f) ? (1.0 - origFuel) * 0.5f : 0.0f; + smokeEmit = (smokeEmit + 0.5f) * (origFuel - fuel(i, j, k)) * 0.1f * flameSmoke; + density(i, j, k) += smokeEmit; + clamp(density(i, j, k), (Real)0.0f, (Real)1.0f); + + // Set fluid temperature from the flame temperature profile + if (heat && flame) + (*heat)(i, j, k) = (1.0f - flame) * ignitionTemp + flame * maxTemp; + + // Mix new color + if (smokeEmit > VECTOR_EPSILON) { + float smokeFactor = density(i, j, k) / (origSmoke + smokeEmit); + if (red) + (*red)(i, j, k) = ((*red)(i, j, k) + flameSmokeColor.x * smokeEmit) * smokeFactor; + if (green) + (*green)(i, j, k) = ((*green)(i, j, k) + flameSmokeColor.y * smokeEmit) * smokeFactor; + if (blue) + (*blue)(i, j, k) = ((*blue)(i, j, k) + flameSmokeColor.z * smokeEmit) * smokeFactor; + } + } + inline Grid<Real> &getArg0() + { + return fuel; + } + typedef Grid<Real> type0; + inline Grid<Real> &getArg1() + { + return density; + } + typedef Grid<Real> type1; + inline Grid<Real> &getArg2() + { + return react; + } + typedef Grid<Real> type2; + inline Grid<Real> *getArg3() + { + return red; + } + typedef Grid<Real> type3; + inline Grid<Real> *getArg4() + { + return green; + } + typedef Grid<Real> type4; + inline Grid<Real> *getArg5() + { + return blue; + } + typedef Grid<Real> type5; + inline Grid<Real> *getArg6() + { + return heat; + } + typedef Grid<Real> type6; + inline Real &getArg7() + { + return burningRate; + } + typedef Real type7; + inline Real &getArg8() + { + return flameSmoke; + } + typedef Real type8; + inline Real &getArg9() + { + return ignitionTemp; + } + typedef Real type9; + inline Real &getArg10() + { + return maxTemp; + } + typedef Real type10; + inline Real &getArg11() + { + return dt; + } + typedef Real type11; + inline Vec3 &getArg12() + { + return flameSmokeColor; + } + typedef Vec3 type12; + void runMessage() + { + debMsg("Executing kernel KnProcessBurn ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, + j, + k, + fuel, + density, + react, + red, + green, + blue, + heat, + burningRate, + flameSmoke, + ignitionTemp, + maxTemp, + dt, + flameSmokeColor); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, + j, + k, + fuel, + density, + react, + red, + green, + blue, + heat, + burningRate, + flameSmoke, + ignitionTemp, + maxTemp, + dt, + flameSmokeColor); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + Grid<Real> &fuel; + Grid<Real> &density; + Grid<Real> &react; + Grid<Real> *red; + Grid<Real> *green; + Grid<Real> *blue; + Grid<Real> *heat; + Real burningRate; + Real flameSmoke; + Real ignitionTemp; + Real maxTemp; + Real dt; + Vec3 flameSmokeColor; +}; + +void processBurn(Grid<Real> &fuel, + Grid<Real> &density, + Grid<Real> &react, + Grid<Real> *red = NULL, + Grid<Real> *green = NULL, + Grid<Real> *blue = NULL, + Grid<Real> *heat = NULL, + Real burningRate = 0.75f, + Real flameSmoke = 1.0f, + Real ignitionTemp = 1.25f, + Real maxTemp = 1.75f, + Vec3 flameSmokeColor = Vec3(0.7f, 0.7f, 0.7f)) +{ + Real dt = fuel.getParent()->getDt(); + KnProcessBurn(fuel, + density, + react, + red, + green, + blue, + heat, + burningRate, + flameSmoke, + ignitionTemp, + maxTemp, + dt, + flameSmokeColor); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "processBurn", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &fuel = *_args.getPtr<Grid<Real>>("fuel", 0, &_lock); + Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + Grid<Real> &react = *_args.getPtr<Grid<Real>>("react", 2, &_lock); + Grid<Real> *red = _args.getPtrOpt<Grid<Real>>("red", 3, NULL, &_lock); + Grid<Real> *green = _args.getPtrOpt<Grid<Real>>("green", 4, NULL, &_lock); + Grid<Real> *blue = _args.getPtrOpt<Grid<Real>>("blue", 5, NULL, &_lock); + Grid<Real> *heat = _args.getPtrOpt<Grid<Real>>("heat", 6, NULL, &_lock); + Real burningRate = _args.getOpt<Real>("burningRate", 7, 0.75f, &_lock); + Real flameSmoke = _args.getOpt<Real>("flameSmoke", 8, 1.0f, &_lock); + Real ignitionTemp = _args.getOpt<Real>("ignitionTemp", 9, 1.25f, &_lock); + Real maxTemp = _args.getOpt<Real>("maxTemp", 10, 1.75f, &_lock); + Vec3 flameSmokeColor = _args.getOpt<Vec3>( + "flameSmokeColor", 11, Vec3(0.7f, 0.7f, 0.7f), &_lock); + _retval = getPyNone(); + processBurn(fuel, + density, + react, + red, + green, + blue, + heat, + burningRate, + flameSmoke, + ignitionTemp, + maxTemp, + flameSmokeColor); + _args.check(); + } + pbFinalizePlugin(parent, "processBurn", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("processBurn", e.what()); + return 0; + } +} +static const Pb::Register _RP_processBurn("", "processBurn", _W_0); +extern "C" { +void PbRegister_processBurn() +{ + KEEP_UNUSED(_RP_processBurn); +} +} + +struct KnUpdateFlame : public KernelBase { + KnUpdateFlame(const Grid<Real> &react, Grid<Real> &flame) + : KernelBase(&react, 1), react(react), flame(flame) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const Grid<Real> &react, Grid<Real> &flame) const + { + if (react(i, j, k) > 0.0f) + flame(i, j, k) = pow(react(i, j, k), 0.5f); + else + flame(i, j, k) = 0.0f; + } + inline const Grid<Real> &getArg0() + { + return react; + } + typedef Grid<Real> type0; + inline Grid<Real> &getArg1() + { + return flame; + } + typedef Grid<Real> type1; + void runMessage() + { + debMsg("Executing kernel KnUpdateFlame ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, react, flame); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, react, flame); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const Grid<Real> &react; + Grid<Real> &flame; +}; + +void updateFlame(const Grid<Real> &react, Grid<Real> &flame) +{ + KnUpdateFlame(react, flame); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "updateFlame", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<Real> &react = *_args.getPtr<Grid<Real>>("react", 0, &_lock); + Grid<Real> &flame = *_args.getPtr<Grid<Real>>("flame", 1, &_lock); + _retval = getPyNone(); + updateFlame(react, flame); + _args.check(); + } + pbFinalizePlugin(parent, "updateFlame", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("updateFlame", e.what()); + return 0; + } +} +static const Pb::Register _RP_updateFlame("", "updateFlame", _W_1); +extern "C" { +void PbRegister_updateFlame() +{ + KEEP_UNUSED(_RP_updateFlame); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/flip.cpp b/extern/mantaflow/preprocessed/plugin/flip.cpp new file mode 100644 index 00000000000..f6d082900b5 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/flip.cpp @@ -0,0 +1,2819 @@ + + +// 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 + * + * FLIP (fluid implicit particles) + * for use with particle data fields + * + ******************************************************************************/ + +#include "particle.h" +#include "grid.h" +#include "commonkernels.h" +#include "randomstream.h" +#include "levelset.h" +#include "shapes.h" +#include "matrixbase.h" + +using namespace std; +namespace Manta { + +// init + +//! note - this is a simplified version , sampleLevelsetWithParticles has more functionality + +void sampleFlagsWithParticles(const FlagGrid &flags, + BasicParticleSystem &parts, + const int discretization, + const Real randomness) +{ + const bool is3D = flags.is3D(); + const Real jlen = randomness / discretization; + const Vec3 disp(1.0 / discretization, 1.0 / discretization, 1.0 / discretization); + RandomStream mRand(9832); + + FOR_IJK_BND(flags, 0) + { + if (flags.isObstacle(i, j, k)) + continue; + if (flags.isFluid(i, j, k)) { + const Vec3 pos(i, j, k); + for (int dk = 0; dk < (is3D ? discretization : 1); dk++) + for (int dj = 0; dj < discretization; dj++) + for (int di = 0; di < discretization; di++) { + Vec3 subpos = pos + disp * Vec3(0.5 + di, 0.5 + dj, 0.5 + dk); + subpos += jlen * (Vec3(1, 1, 1) - 2.0 * mRand.getVec3()); + if (!is3D) + subpos[2] = 0.5; + parts.addBuffered(subpos); + } + } + } + parts.insertBufferedParticles(); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "sampleFlagsWithParticles", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 1, &_lock); + const int discretization = _args.get<int>("discretization", 2, &_lock); + const Real randomness = _args.get<Real>("randomness", 3, &_lock); + _retval = getPyNone(); + sampleFlagsWithParticles(flags, parts, discretization, randomness); + _args.check(); + } + pbFinalizePlugin(parent, "sampleFlagsWithParticles", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("sampleFlagsWithParticles", e.what()); + return 0; + } +} +static const Pb::Register _RP_sampleFlagsWithParticles("", "sampleFlagsWithParticles", _W_0); +extern "C" { +void PbRegister_sampleFlagsWithParticles() +{ + KEEP_UNUSED(_RP_sampleFlagsWithParticles); +} +} + +//! sample a level set with particles, use reset to clear the particle buffer, +//! and skipEmpty for a continuous inflow (in the latter case, only empty cells will +//! be re-filled once they empty when calling sampleLevelsetWithParticles during +//! the main loop). + +void sampleLevelsetWithParticles(const LevelsetGrid &phi, + const FlagGrid &flags, + BasicParticleSystem &parts, + const int discretization, + const Real randomness, + const bool reset = false, + const bool refillEmpty = false, + const int particleFlag = -1) +{ + const bool is3D = phi.is3D(); + const Real jlen = randomness / discretization; + const Vec3 disp(1.0 / discretization, 1.0 / discretization, 1.0 / discretization); + RandomStream mRand(9832); + + if (reset) { + parts.clear(); + parts.doCompress(); + } + + FOR_IJK_BND(phi, 0) + { + if (flags.isObstacle(i, j, k)) + continue; + if (refillEmpty && flags.isFluid(i, j, k)) + continue; + if (phi(i, j, k) < 1.733) { + const Vec3 pos(i, j, k); + for (int dk = 0; dk < (is3D ? discretization : 1); dk++) + for (int dj = 0; dj < discretization; dj++) + for (int di = 0; di < discretization; di++) { + Vec3 subpos = pos + disp * Vec3(0.5 + di, 0.5 + dj, 0.5 + dk); + subpos += jlen * (Vec3(1, 1, 1) - 2.0 * mRand.getVec3()); + if (!is3D) + subpos[2] = 0.5; + if (phi.getInterpolated(subpos) > 0.) + continue; + if (particleFlag < 0) { + parts.addBuffered(subpos); + } + else { + parts.addBuffered(subpos, particleFlag); + } + } + } + } + + parts.insertBufferedParticles(); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "sampleLevelsetWithParticles", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const LevelsetGrid &phi = *_args.getPtr<LevelsetGrid>("phi", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 2, &_lock); + const int discretization = _args.get<int>("discretization", 3, &_lock); + const Real randomness = _args.get<Real>("randomness", 4, &_lock); + const bool reset = _args.getOpt<bool>("reset", 5, false, &_lock); + const bool refillEmpty = _args.getOpt<bool>("refillEmpty", 6, false, &_lock); + const int particleFlag = _args.getOpt<int>("particleFlag", 7, -1, &_lock); + _retval = getPyNone(); + sampleLevelsetWithParticles( + phi, flags, parts, discretization, randomness, reset, refillEmpty, particleFlag); + _args.check(); + } + pbFinalizePlugin(parent, "sampleLevelsetWithParticles", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("sampleLevelsetWithParticles", e.what()); + return 0; + } +} +static const Pb::Register _RP_sampleLevelsetWithParticles("", "sampleLevelsetWithParticles", _W_1); +extern "C" { +void PbRegister_sampleLevelsetWithParticles() +{ + KEEP_UNUSED(_RP_sampleLevelsetWithParticles); +} +} + +//! sample a shape with particles, use reset to clear the particle buffer, +//! and skipEmpty for a continuous inflow (in the latter case, only empty cells will +//! be re-filled once they empty when calling sampleShapeWithParticles during +//! the main loop). + +void sampleShapeWithParticles(const Shape &shape, + const FlagGrid &flags, + BasicParticleSystem &parts, + const int discretization, + const Real randomness, + const bool reset = false, + const bool refillEmpty = false, + const LevelsetGrid *exclude = NULL) +{ + const bool is3D = flags.is3D(); + const Real jlen = randomness / discretization; + const Vec3 disp(1.0 / discretization, 1.0 / discretization, 1.0 / discretization); + RandomStream mRand(9832); + + if (reset) { + parts.clear(); + parts.doCompress(); + } + + FOR_IJK_BND(flags, 0) + { + if (flags.isObstacle(i, j, k)) + continue; + if (refillEmpty && flags.isFluid(i, j, k)) + continue; + const Vec3 pos(i, j, k); + for (int dk = 0; dk < (is3D ? discretization : 1); dk++) + for (int dj = 0; dj < discretization; dj++) + for (int di = 0; di < discretization; di++) { + Vec3 subpos = pos + disp * Vec3(0.5 + di, 0.5 + dj, 0.5 + dk); + subpos += jlen * (Vec3(1, 1, 1) - 2.0 * mRand.getVec3()); + if (!is3D) + subpos[2] = 0.5; + if (exclude && exclude->getInterpolated(subpos) <= 0.) + continue; + if (!shape.isInside(subpos)) + continue; + parts.addBuffered(subpos); + } + } + + parts.insertBufferedParticles(); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "sampleShapeWithParticles", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Shape &shape = *_args.getPtr<Shape>("shape", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 2, &_lock); + const int discretization = _args.get<int>("discretization", 3, &_lock); + const Real randomness = _args.get<Real>("randomness", 4, &_lock); + const bool reset = _args.getOpt<bool>("reset", 5, false, &_lock); + const bool refillEmpty = _args.getOpt<bool>("refillEmpty", 6, false, &_lock); + const LevelsetGrid *exclude = _args.getPtrOpt<LevelsetGrid>("exclude", 7, NULL, &_lock); + _retval = getPyNone(); + sampleShapeWithParticles( + shape, flags, parts, discretization, randomness, reset, refillEmpty, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "sampleShapeWithParticles", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("sampleShapeWithParticles", e.what()); + return 0; + } +} +static const Pb::Register _RP_sampleShapeWithParticles("", "sampleShapeWithParticles", _W_2); +extern "C" { +void PbRegister_sampleShapeWithParticles() +{ + KEEP_UNUSED(_RP_sampleShapeWithParticles); +} +} + +//! mark fluid cells and helpers +struct knClearFluidFlags : public KernelBase { + knClearFluidFlags(FlagGrid &flags, int dummy = 0) + : KernelBase(&flags, 0), flags(flags), dummy(dummy) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, FlagGrid &flags, int dummy = 0) const + { + if (flags.isFluid(i, j, k)) { + flags(i, j, k) = (flags(i, j, k) | FlagGrid::TypeEmpty) & ~FlagGrid::TypeFluid; + } + } + inline FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline int &getArg1() + { + return dummy; + } + typedef int type1; + void runMessage() + { + debMsg("Executing kernel knClearFluidFlags ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, dummy); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, dummy); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + FlagGrid &flags; + int dummy; +}; + +struct knSetNbObstacle : public KernelBase { + knSetNbObstacle(FlagGrid &nflags, const FlagGrid &flags, const Grid<Real> &phiObs) + : KernelBase(&nflags, 1), nflags(nflags), flags(flags), phiObs(phiObs) + { + runMessage(); + run(); + } + inline void op( + int i, int j, int k, FlagGrid &nflags, const FlagGrid &flags, const Grid<Real> &phiObs) const + { + if (phiObs(i, j, k) > 0.) + return; + if (flags.isEmpty(i, j, k)) { + bool set = false; + if ((flags.isFluid(i - 1, j, k)) && (phiObs(i + 1, j, k) <= 0.)) + set = true; + if ((flags.isFluid(i + 1, j, k)) && (phiObs(i - 1, j, k) <= 0.)) + set = true; + if ((flags.isFluid(i, j - 1, k)) && (phiObs(i, j + 1, k) <= 0.)) + set = true; + if ((flags.isFluid(i, j + 1, k)) && (phiObs(i, j - 1, k) <= 0.)) + set = true; + if (flags.is3D()) { + if ((flags.isFluid(i, j, k - 1)) && (phiObs(i, j, k + 1) <= 0.)) + set = true; + if ((flags.isFluid(i, j, k + 1)) && (phiObs(i, j, k - 1) <= 0.)) + set = true; + } + if (set) + nflags(i, j, k) = (flags(i, j, k) | FlagGrid::TypeFluid) & ~FlagGrid::TypeEmpty; + } + } + inline FlagGrid &getArg0() + { + return nflags; + } + typedef FlagGrid type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const Grid<Real> &getArg2() + { + return phiObs; + } + typedef Grid<Real> type2; + void runMessage() + { + debMsg("Executing kernel knSetNbObstacle ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, nflags, flags, phiObs); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, nflags, flags, phiObs); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + FlagGrid &nflags; + const FlagGrid &flags; + const Grid<Real> &phiObs; +}; +void markFluidCells(const BasicParticleSystem &parts, + FlagGrid &flags, + const Grid<Real> *phiObs = NULL, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + // remove all fluid cells + knClearFluidFlags(flags, 0); + + // mark all particles in flaggrid as fluid + for (IndexInt idx = 0; idx < parts.size(); idx++) { + if (!parts.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) + continue; + Vec3i p = toVec3i(parts.getPos(idx)); + if (flags.isInBounds(p) && flags.isEmpty(p)) + flags(p) = (flags(p) | FlagGrid::TypeFluid) & ~FlagGrid::TypeEmpty; + } + + // special for second order obstacle BCs, check empty cells in boundary region + if (phiObs) { + FlagGrid tmp(flags); + knSetNbObstacle(tmp, flags, *phiObs); + flags.swap(tmp); + } +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "markFluidCells", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + const Grid<Real> *phiObs = _args.getPtrOpt<Grid<Real>>("phiObs", 2, NULL, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 3, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 4, 0, &_lock); + _retval = getPyNone(); + markFluidCells(parts, flags, phiObs, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "markFluidCells", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("markFluidCells", e.what()); + return 0; + } +} +static const Pb::Register _RP_markFluidCells("", "markFluidCells", _W_3); +extern "C" { +void PbRegister_markFluidCells() +{ + KEEP_UNUSED(_RP_markFluidCells); +} +} + +// for testing purposes only... +void testInitGridWithPos(Grid<Real> &grid) +{ + FOR_IJK(grid) + { + grid(i, j, k) = norm(Vec3(i, j, k)); + } +} +static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "testInitGridWithPos", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &grid = *_args.getPtr<Grid<Real>>("grid", 0, &_lock); + _retval = getPyNone(); + testInitGridWithPos(grid); + _args.check(); + } + pbFinalizePlugin(parent, "testInitGridWithPos", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("testInitGridWithPos", e.what()); + return 0; + } +} +static const Pb::Register _RP_testInitGridWithPos("", "testInitGridWithPos", _W_4); +extern "C" { +void PbRegister_testInitGridWithPos() +{ + KEEP_UNUSED(_RP_testInitGridWithPos); +} +} + +//! helper to calculate particle radius factor to cover the diagonal of a cell in 2d/3d +inline Real calculateRadiusFactor(const Grid<Real> &grid, Real factor) +{ + return (grid.is3D() ? sqrt(3.) : sqrt(2.)) * + (factor + .01); // note, a 1% safety factor is added here +} + +//! re-sample particles based on an input levelset +// optionally skip seeding new particles in "exclude" SDF + +void adjustNumber(BasicParticleSystem &parts, + const MACGrid &vel, + const FlagGrid &flags, + int minParticles, + int maxParticles, + const LevelsetGrid &phi, + Real radiusFactor = 1., + Real narrowBand = -1., + const Grid<Real> *exclude = NULL) +{ + // which levelset to use as threshold + const Real SURFACE_LS = -1.0 * calculateRadiusFactor(phi, radiusFactor); + Grid<int> tmp(vel.getParent()); + std::ostringstream out; + + // count particles in cells, and delete excess particles + for (IndexInt idx = 0; idx < (int)parts.size(); idx++) { + if (parts.isActive(idx)) { + Vec3i p = toVec3i(parts.getPos(idx)); + if (!tmp.isInBounds(p)) { + parts.kill(idx); // out of domain, remove + continue; + } + + Real phiv = phi.getInterpolated(parts.getPos(idx)); + if (phiv > 0) { + parts.kill(idx); + continue; + } + if (narrowBand > 0. && phiv < -narrowBand) { + parts.kill(idx); + continue; + } + + bool atSurface = false; + if (phiv > SURFACE_LS) + atSurface = true; + int num = tmp(p); + + // dont delete particles in non fluid cells here, the particles are "always right" + if (num > maxParticles && (!atSurface)) { + parts.kill(idx); + } + else { + tmp(p) = num + 1; + } + } + } + + // seed new particles + RandomStream mRand(9832); + FOR_IJK(tmp) + { + int cnt = tmp(i, j, k); + + // skip cells near surface + if (phi(i, j, k) > SURFACE_LS) + continue; + if (narrowBand > 0. && phi(i, j, k) < -narrowBand) { + continue; + } + if (exclude && ((*exclude)(i, j, k) < 0.)) { + continue; + } + + if (flags.isFluid(i, j, k) && cnt < minParticles) { + for (int m = cnt; m < minParticles; m++) { + Vec3 pos = Vec3(i, j, k) + mRand.getVec3(); + // Vec3 pos (i + 0.5, j + 0.5, k + 0.5); // cell center + parts.addBuffered(pos); + } + } + } + + parts.doCompress(); + parts.insertBufferedParticles(); +} +static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "adjustNumber", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 2, &_lock); + int minParticles = _args.get<int>("minParticles", 3, &_lock); + int maxParticles = _args.get<int>("maxParticles", 4, &_lock); + const LevelsetGrid &phi = *_args.getPtr<LevelsetGrid>("phi", 5, &_lock); + Real radiusFactor = _args.getOpt<Real>("radiusFactor", 6, 1., &_lock); + Real narrowBand = _args.getOpt<Real>("narrowBand", 7, -1., &_lock); + const Grid<Real> *exclude = _args.getPtrOpt<Grid<Real>>("exclude", 8, NULL, &_lock); + _retval = getPyNone(); + adjustNumber( + parts, vel, flags, minParticles, maxParticles, phi, radiusFactor, narrowBand, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "adjustNumber", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("adjustNumber", e.what()); + return 0; + } +} +static const Pb::Register _RP_adjustNumber("", "adjustNumber", _W_5); +extern "C" { +void PbRegister_adjustNumber() +{ + KEEP_UNUSED(_RP_adjustNumber); +} +} + +// simple and slow helper conversion to show contents of int grids like a real grid in the ui +// (use eg to quickly display contents of the particle-index grid) + +void debugIntToReal(const Grid<int> &source, Grid<Real> &dest, Real factor = 1.) +{ + FOR_IJK(source) + { + dest(i, j, k) = (Real)source(i, j, k) * factor; + } +} +static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "debugIntToReal", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<int> &source = *_args.getPtr<Grid<int>>("source", 0, &_lock); + Grid<Real> &dest = *_args.getPtr<Grid<Real>>("dest", 1, &_lock); + Real factor = _args.getOpt<Real>("factor", 2, 1., &_lock); + _retval = getPyNone(); + debugIntToReal(source, dest, factor); + _args.check(); + } + pbFinalizePlugin(parent, "debugIntToReal", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("debugIntToReal", e.what()); + return 0; + } +} +static const Pb::Register _RP_debugIntToReal("", "debugIntToReal", _W_6); +extern "C" { +void PbRegister_debugIntToReal() +{ + KEEP_UNUSED(_RP_debugIntToReal); +} +} + +// build a grid that contains indices for a particle system +// the particles in a cell i,j,k are particles[index(i,j,k)] to particles[index(i+1,j,k)-1] +// (ie, particles[index(i+1,j,k)] already belongs to cell i+1,j,k) + +void gridParticleIndex(const BasicParticleSystem &parts, + ParticleIndexSystem &indexSys, + const FlagGrid &flags, + Grid<int> &index, + Grid<int> *counter = NULL) +{ + bool delCounter = false; + if (!counter) { + counter = new Grid<int>(flags.getParent()); + delCounter = true; + } + else { + counter->clear(); + } + + // count particles in cells, and delete excess particles + index.clear(); + int inactive = 0; + for (IndexInt idx = 0; idx < (IndexInt)parts.size(); idx++) { + if (parts.isActive(idx)) { + // check index for validity... + Vec3i p = toVec3i(parts.getPos(idx)); + if (!index.isInBounds(p)) { + inactive++; + continue; + } + + index(p)++; + } + else { + inactive++; + } + } + + // note - this one might be smaller... + indexSys.resize(parts.size() - inactive); + + // convert per cell number to continuous index + IndexInt idx = 0; + FOR_IJK(index) + { + int num = index(i, j, k); + index(i, j, k) = idx; + idx += num; + } + + // add particles to indexed array, we still need a per cell particle counter + for (IndexInt idx = 0; idx < (IndexInt)parts.size(); idx++) { + if (!parts.isActive(idx)) + continue; + Vec3i p = toVec3i(parts.getPos(idx)); + if (!index.isInBounds(p)) { + continue; + } + + // initialize position and index into original array + // indexSys[ index(p)+(*counter)(p) ].pos = parts[idx].pos; + indexSys[index(p) + (*counter)(p)].sourceIndex = idx; + (*counter)(p)++; + } + + if (delCounter) + delete counter; +} +static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "gridParticleIndex", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + ParticleIndexSystem &indexSys = *_args.getPtr<ParticleIndexSystem>("indexSys", 1, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 2, &_lock); + Grid<int> &index = *_args.getPtr<Grid<int>>("index", 3, &_lock); + Grid<int> *counter = _args.getPtrOpt<Grid<int>>("counter", 4, NULL, &_lock); + _retval = getPyNone(); + gridParticleIndex(parts, indexSys, flags, index, counter); + _args.check(); + } + pbFinalizePlugin(parent, "gridParticleIndex", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("gridParticleIndex", e.what()); + return 0; + } +} +static const Pb::Register _RP_gridParticleIndex("", "gridParticleIndex", _W_7); +extern "C" { +void PbRegister_gridParticleIndex() +{ + KEEP_UNUSED(_RP_gridParticleIndex); +} +} + +struct ComputeUnionLevelsetPindex : public KernelBase { + ComputeUnionLevelsetPindex(const Grid<int> &index, + const BasicParticleSystem &parts, + const ParticleIndexSystem &indexSys, + LevelsetGrid &phi, + const Real radius, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(&index, 0), + index(index), + parts(parts), + indexSys(indexSys), + phi(phi), + radius(radius), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const Grid<int> &index, + const BasicParticleSystem &parts, + const ParticleIndexSystem &indexSys, + LevelsetGrid &phi, + const Real radius, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + const Vec3 gridPos = Vec3(i, j, k) + Vec3(0.5); // shifted by half cell + Real phiv = radius * 1.0; // outside + + int r = int(radius) + 1; + int rZ = phi.is3D() ? r : 0; + for (int zj = k - rZ; zj <= k + rZ; zj++) + for (int yj = j - r; yj <= j + r; yj++) + for (int xj = i - r; xj <= i + r; xj++) { + if (!phi.isInBounds(Vec3i(xj, yj, zj))) + continue; + + // note, for the particle indices in indexSys the access is periodic (ie, dont skip for + // eg inBounds(sx,10,10) + IndexInt isysIdxS = index.index(xj, yj, zj); + IndexInt pStart = index(isysIdxS), pEnd = 0; + if (phi.isInBounds(isysIdxS + 1)) + pEnd = index(isysIdxS + 1); + else + pEnd = indexSys.size(); + + // now loop over particles in cell + for (IndexInt p = pStart; p < pEnd; ++p) { + const int psrc = indexSys[p].sourceIndex; + if (ptype && ((*ptype)[psrc] & exclude)) + continue; + const Vec3 pos = parts[psrc].pos; + phiv = std::min(phiv, fabs(norm(gridPos - pos)) - radius); + } + } + phi(i, j, k) = phiv; + } + inline const Grid<int> &getArg0() + { + return index; + } + typedef Grid<int> type0; + inline const BasicParticleSystem &getArg1() + { + return parts; + } + typedef BasicParticleSystem type1; + inline const ParticleIndexSystem &getArg2() + { + return indexSys; + } + typedef ParticleIndexSystem type2; + inline LevelsetGrid &getArg3() + { + return phi; + } + typedef LevelsetGrid type3; + inline const Real &getArg4() + { + return radius; + } + typedef Real type4; + inline const ParticleDataImpl<int> *getArg5() + { + return ptype; + } + typedef ParticleDataImpl<int> type5; + inline const int &getArg6() + { + return exclude; + } + typedef int type6; + void runMessage() + { + debMsg("Executing kernel ComputeUnionLevelsetPindex ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, index, parts, indexSys, phi, radius, ptype, exclude); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, index, parts, indexSys, phi, radius, ptype, exclude); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const Grid<int> &index; + const BasicParticleSystem &parts; + const ParticleIndexSystem &indexSys; + LevelsetGrid φ + const Real radius; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; + +void unionParticleLevelset(const BasicParticleSystem &parts, + const ParticleIndexSystem &indexSys, + const FlagGrid &flags, + const Grid<int> &index, + LevelsetGrid &phi, + const Real radiusFactor = 1., + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + // use half a cell diagonal as base radius + const Real radius = 0.5 * calculateRadiusFactor(phi, radiusFactor); + // no reset of phi necessary here + ComputeUnionLevelsetPindex(index, parts, indexSys, phi, radius, ptype, exclude); + + phi.setBound(0.5, 0); +} +static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "unionParticleLevelset", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + const ParticleIndexSystem &indexSys = *_args.getPtr<ParticleIndexSystem>( + "indexSys", 1, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 2, &_lock); + const Grid<int> &index = *_args.getPtr<Grid<int>>("index", 3, &_lock); + LevelsetGrid &phi = *_args.getPtr<LevelsetGrid>("phi", 4, &_lock); + const Real radiusFactor = _args.getOpt<Real>("radiusFactor", 5, 1., &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 6, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 7, 0, &_lock); + _retval = getPyNone(); + unionParticleLevelset(parts, indexSys, flags, index, phi, radiusFactor, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "unionParticleLevelset", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("unionParticleLevelset", e.what()); + return 0; + } +} +static const Pb::Register _RP_unionParticleLevelset("", "unionParticleLevelset", _W_8); +extern "C" { +void PbRegister_unionParticleLevelset() +{ + KEEP_UNUSED(_RP_unionParticleLevelset); +} +} + +//! kernel for computing averaged particle level set weights + +struct ComputeAveragedLevelsetWeight : public KernelBase { + ComputeAveragedLevelsetWeight(const BasicParticleSystem &parts, + const Grid<int> &index, + const ParticleIndexSystem &indexSys, + LevelsetGrid &phi, + const Real radius, + const ParticleDataImpl<int> *ptype, + const int exclude, + Grid<Vec3> *save_pAcc = NULL, + Grid<Real> *save_rAcc = NULL) + : KernelBase(&index, 0), + parts(parts), + index(index), + indexSys(indexSys), + phi(phi), + radius(radius), + ptype(ptype), + exclude(exclude), + save_pAcc(save_pAcc), + save_rAcc(save_rAcc) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const BasicParticleSystem &parts, + const Grid<int> &index, + const ParticleIndexSystem &indexSys, + LevelsetGrid &phi, + const Real radius, + const ParticleDataImpl<int> *ptype, + const int exclude, + Grid<Vec3> *save_pAcc = NULL, + Grid<Real> *save_rAcc = NULL) const + { + const Vec3 gridPos = Vec3(i, j, k) + Vec3(0.5); // shifted by half cell + Real phiv = radius * 1.0; // outside + + // loop over neighborhood, similar to ComputeUnionLevelsetPindex + const Real sradiusInv = 1. / (4. * radius * radius); + int r = int(1. * radius) + 1; + int rZ = phi.is3D() ? r : 0; + // accumulators + Real wacc = 0.; + Vec3 pacc = Vec3(0.); + Real racc = 0.; + + for (int zj = k - rZ; zj <= k + rZ; zj++) + for (int yj = j - r; yj <= j + r; yj++) + for (int xj = i - r; xj <= i + r; xj++) { + if (!phi.isInBounds(Vec3i(xj, yj, zj))) + continue; + + IndexInt isysIdxS = index.index(xj, yj, zj); + IndexInt pStart = index(isysIdxS), pEnd = 0; + if (phi.isInBounds(isysIdxS + 1)) + pEnd = index(isysIdxS + 1); + else + pEnd = indexSys.size(); + for (IndexInt p = pStart; p < pEnd; ++p) { + IndexInt psrc = indexSys[p].sourceIndex; + if (ptype && ((*ptype)[psrc] & exclude)) + continue; + + Vec3 pos = parts[psrc].pos; + Real s = normSquare(gridPos - pos) * sradiusInv; + // Real w = std::max(0., cubed(1.-s) ); + Real w = std::max(0., (1. - s)); // a bit smoother + wacc += w; + racc += radius * w; + pacc += pos * w; + } + } + + if (wacc > VECTOR_EPSILON) { + racc /= wacc; + pacc /= wacc; + phiv = fabs(norm(gridPos - pacc)) - racc; + + if (save_pAcc) + (*save_pAcc)(i, j, k) = pacc; + if (save_rAcc) + (*save_rAcc)(i, j, k) = racc; + } + phi(i, j, k) = phiv; + } + inline const BasicParticleSystem &getArg0() + { + return parts; + } + typedef BasicParticleSystem type0; + inline const Grid<int> &getArg1() + { + return index; + } + typedef Grid<int> type1; + inline const ParticleIndexSystem &getArg2() + { + return indexSys; + } + typedef ParticleIndexSystem type2; + inline LevelsetGrid &getArg3() + { + return phi; + } + typedef LevelsetGrid type3; + inline const Real &getArg4() + { + return radius; + } + typedef Real type4; + inline const ParticleDataImpl<int> *getArg5() + { + return ptype; + } + typedef ParticleDataImpl<int> type5; + inline const int &getArg6() + { + return exclude; + } + typedef int type6; + inline Grid<Vec3> *getArg7() + { + return save_pAcc; + } + typedef Grid<Vec3> type7; + inline Grid<Real> *getArg8() + { + return save_rAcc; + } + typedef Grid<Real> type8; + void runMessage() + { + debMsg("Executing kernel ComputeAveragedLevelsetWeight ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, parts, index, indexSys, phi, radius, ptype, exclude, save_pAcc, save_rAcc); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, parts, index, indexSys, phi, radius, ptype, exclude, save_pAcc, save_rAcc); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const BasicParticleSystem &parts; + const Grid<int> &index; + const ParticleIndexSystem &indexSys; + LevelsetGrid φ + const Real radius; + const ParticleDataImpl<int> *ptype; + const int exclude; + Grid<Vec3> *save_pAcc; + Grid<Real> *save_rAcc; +}; + +template<class T> T smoothingValue(const Grid<T> val, int i, int j, int k, T center) +{ + return val(i, j, k); +} + +// smoothing, and + +template<class T> struct knSmoothGrid : public KernelBase { + knSmoothGrid(const Grid<T> &me, Grid<T> &tmp, Real factor) + : KernelBase(&me, 1), me(me), tmp(tmp), factor(factor) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const Grid<T> &me, Grid<T> &tmp, Real factor) const + { + T val = me(i, j, k) + me(i + 1, j, k) + me(i - 1, j, k) + me(i, j + 1, k) + me(i, j - 1, k); + if (me.is3D()) { + val += me(i, j, k + 1) + me(i, j, k - 1); + } + tmp(i, j, k) = val * factor; + } + inline const Grid<T> &getArg0() + { + return me; + } + typedef Grid<T> type0; + inline Grid<T> &getArg1() + { + return tmp; + } + typedef Grid<T> type1; + inline Real &getArg2() + { + return factor; + } + typedef Real type2; + void runMessage() + { + debMsg("Executing kernel knSmoothGrid ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, me, tmp, factor); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, me, tmp, factor); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const Grid<T> &me; + Grid<T> &tmp; + Real factor; +}; + +template<class T> struct knSmoothGridNeg : public KernelBase { + knSmoothGridNeg(const Grid<T> &me, Grid<T> &tmp, Real factor) + : KernelBase(&me, 1), me(me), tmp(tmp), factor(factor) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const Grid<T> &me, Grid<T> &tmp, Real factor) const + { + T val = me(i, j, k) + me(i + 1, j, k) + me(i - 1, j, k) + me(i, j + 1, k) + me(i, j - 1, k); + if (me.is3D()) { + val += me(i, j, k + 1) + me(i, j, k - 1); + } + val *= factor; + if (val < tmp(i, j, k)) + tmp(i, j, k) = val; + else + tmp(i, j, k) = me(i, j, k); + } + inline const Grid<T> &getArg0() + { + return me; + } + typedef Grid<T> type0; + inline Grid<T> &getArg1() + { + return tmp; + } + typedef Grid<T> type1; + inline Real &getArg2() + { + return factor; + } + typedef Real type2; + void runMessage() + { + debMsg("Executing kernel knSmoothGridNeg ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, me, tmp, factor); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, me, tmp, factor); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const Grid<T> &me; + Grid<T> &tmp; + Real factor; +}; + +//! Zhu & Bridson particle level set creation + +void averagedParticleLevelset(const BasicParticleSystem &parts, + const ParticleIndexSystem &indexSys, + const FlagGrid &flags, + const Grid<int> &index, + LevelsetGrid &phi, + const Real radiusFactor = 1., + const int smoothen = 1, + const int smoothenNeg = 1, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + // use half a cell diagonal as base radius + const Real radius = 0.5 * calculateRadiusFactor(phi, radiusFactor); + ComputeAveragedLevelsetWeight(parts, index, indexSys, phi, radius, ptype, exclude); + + // post-process level-set + for (int i = 0; i < std::max(smoothen, smoothenNeg); ++i) { + LevelsetGrid tmp(flags.getParent()); + if (i < smoothen) { + knSmoothGrid<Real>(phi, tmp, 1. / (phi.is3D() ? 7. : 5.)); + phi.swap(tmp); + } + if (i < smoothenNeg) { + knSmoothGridNeg<Real>(phi, tmp, 1. / (phi.is3D() ? 7. : 5.)); + phi.swap(tmp); + } + } + phi.setBound(0.5, 0); +} +static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "averagedParticleLevelset", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + const ParticleIndexSystem &indexSys = *_args.getPtr<ParticleIndexSystem>( + "indexSys", 1, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 2, &_lock); + const Grid<int> &index = *_args.getPtr<Grid<int>>("index", 3, &_lock); + LevelsetGrid &phi = *_args.getPtr<LevelsetGrid>("phi", 4, &_lock); + const Real radiusFactor = _args.getOpt<Real>("radiusFactor", 5, 1., &_lock); + const int smoothen = _args.getOpt<int>("smoothen", 6, 1, &_lock); + const int smoothenNeg = _args.getOpt<int>("smoothenNeg", 7, 1, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 8, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 9, 0, &_lock); + _retval = getPyNone(); + averagedParticleLevelset( + parts, indexSys, flags, index, phi, radiusFactor, smoothen, smoothenNeg, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "averagedParticleLevelset", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("averagedParticleLevelset", e.what()); + return 0; + } +} +static const Pb::Register _RP_averagedParticleLevelset("", "averagedParticleLevelset", _W_9); +extern "C" { +void PbRegister_averagedParticleLevelset() +{ + KEEP_UNUSED(_RP_averagedParticleLevelset); +} +} + +//! kernel for improvedParticleLevelset + +struct correctLevelset : public KernelBase { + correctLevelset(LevelsetGrid &phi, + const Grid<Vec3> &pAcc, + const Grid<Real> &rAcc, + const Real radius, + const Real t_low, + const Real t_high) + : KernelBase(&phi, 1), + phi(phi), + pAcc(pAcc), + rAcc(rAcc), + radius(radius), + t_low(t_low), + t_high(t_high) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + LevelsetGrid &phi, + const Grid<Vec3> &pAcc, + const Grid<Real> &rAcc, + const Real radius, + const Real t_low, + const Real t_high) const + { + if (rAcc(i, j, k) <= VECTOR_EPSILON) + return; // outside nothing happens + Real x = pAcc(i, j, k).x; + + // create jacobian of pAcc via central differences + Matrix3x3f jacobian = Matrix3x3f(0.5 * (pAcc(i + 1, j, k).x - pAcc(i - 1, j, k).x), + 0.5 * (pAcc(i, j + 1, k).x - pAcc(i, j - 1, k).x), + 0.5 * (pAcc(i, j, k + 1).x - pAcc(i, j, k - 1).x), + 0.5 * (pAcc(i + 1, j, k).y - pAcc(i - 1, j, k).y), + 0.5 * (pAcc(i, j + 1, k).y - pAcc(i, j - 1, k).y), + 0.5 * (pAcc(i, j, k + 1).y - pAcc(i, j, k - 1).y), + 0.5 * (pAcc(i + 1, j, k).z - pAcc(i - 1, j, k).z), + 0.5 * (pAcc(i, j + 1, k).z - pAcc(i, j - 1, k).z), + 0.5 * (pAcc(i, j, k + 1).z - pAcc(i, j, k - 1).z)); + + // compute largest eigenvalue of jacobian + Vec3 EV = jacobian.eigenvalues(); + Real maxEV = std::max(std::max(EV.x, EV.y), EV.z); + + // calculate correction factor + Real correction = 1; + if (maxEV >= t_low) { + Real t = (t_high - maxEV) / (t_high - t_low); + correction = t * t * t - 3 * t * t + 3 * t; + } + correction = (correction < 0) ? + 0 : + correction; // enforce correction factor to [0,1] (not explicitly in paper) + + const Vec3 gridPos = Vec3(i, j, k) + Vec3(0.5); // shifted by half cell + const Real correctedPhi = fabs(norm(gridPos - pAcc(i, j, k))) - rAcc(i, j, k) * correction; + phi(i, j, k) = (correctedPhi > radius) ? + radius : + correctedPhi; // adjust too high outside values when too few particles are + // nearby to make smoothing possible (not in paper) + } + inline LevelsetGrid &getArg0() + { + return phi; + } + typedef LevelsetGrid type0; + inline const Grid<Vec3> &getArg1() + { + return pAcc; + } + typedef Grid<Vec3> type1; + inline const Grid<Real> &getArg2() + { + return rAcc; + } + typedef Grid<Real> type2; + inline const Real &getArg3() + { + return radius; + } + typedef Real type3; + inline const Real &getArg4() + { + return t_low; + } + typedef Real type4; + inline const Real &getArg5() + { + return t_high; + } + typedef Real type5; + void runMessage() + { + debMsg("Executing kernel correctLevelset ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, phi, pAcc, rAcc, radius, t_low, t_high); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, phi, pAcc, rAcc, radius, t_low, t_high); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + LevelsetGrid φ + const Grid<Vec3> &pAcc; + const Grid<Real> &rAcc; + const Real radius; + const Real t_low; + const Real t_high; +}; + +//! Approach from "A unified particle model for fluid-solid interactions" by Solenthaler et al. in +//! 2007 + +void improvedParticleLevelset(const BasicParticleSystem &parts, + const ParticleIndexSystem &indexSys, + const FlagGrid &flags, + const Grid<int> &index, + LevelsetGrid &phi, + const Real radiusFactor = 1., + const int smoothen = 1, + const int smoothenNeg = 1, + const Real t_low = 0.4, + const Real t_high = 3.5, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + // create temporary grids to store values from levelset weight computation + Grid<Vec3> save_pAcc(flags.getParent()); + Grid<Real> save_rAcc(flags.getParent()); + + const Real radius = 0.5 * calculateRadiusFactor( + phi, radiusFactor); // use half a cell diagonal as base radius + ComputeAveragedLevelsetWeight( + parts, index, indexSys, phi, radius, ptype, exclude, &save_pAcc, &save_rAcc); + correctLevelset(phi, save_pAcc, save_rAcc, radius, t_low, t_high); + + // post-process level-set + for (int i = 0; i < std::max(smoothen, smoothenNeg); ++i) { + LevelsetGrid tmp(flags.getParent()); + if (i < smoothen) { + knSmoothGrid<Real>(phi, tmp, 1. / (phi.is3D() ? 7. : 5.)); + phi.swap(tmp); + } + if (i < smoothenNeg) { + knSmoothGridNeg<Real>(phi, tmp, 1. / (phi.is3D() ? 7. : 5.)); + phi.swap(tmp); + } + } + phi.setBound(0.5, 0); +} +static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "improvedParticleLevelset", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + const ParticleIndexSystem &indexSys = *_args.getPtr<ParticleIndexSystem>( + "indexSys", 1, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 2, &_lock); + const Grid<int> &index = *_args.getPtr<Grid<int>>("index", 3, &_lock); + LevelsetGrid &phi = *_args.getPtr<LevelsetGrid>("phi", 4, &_lock); + const Real radiusFactor = _args.getOpt<Real>("radiusFactor", 5, 1., &_lock); + const int smoothen = _args.getOpt<int>("smoothen", 6, 1, &_lock); + const int smoothenNeg = _args.getOpt<int>("smoothenNeg", 7, 1, &_lock); + const Real t_low = _args.getOpt<Real>("t_low", 8, 0.4, &_lock); + const Real t_high = _args.getOpt<Real>("t_high", 9, 3.5, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 10, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 11, 0, &_lock); + _retval = getPyNone(); + improvedParticleLevelset(parts, + indexSys, + flags, + index, + phi, + radiusFactor, + smoothen, + smoothenNeg, + t_low, + t_high, + ptype, + exclude); + _args.check(); + } + pbFinalizePlugin(parent, "improvedParticleLevelset", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("improvedParticleLevelset", e.what()); + return 0; + } +} +static const Pb::Register _RP_improvedParticleLevelset("", "improvedParticleLevelset", _W_10); +extern "C" { +void PbRegister_improvedParticleLevelset() +{ + KEEP_UNUSED(_RP_improvedParticleLevelset); +} +} + +struct knPushOutofObs : public KernelBase { + knPushOutofObs(BasicParticleSystem &parts, + const FlagGrid &flags, + const Grid<Real> &phiObs, + const Real shift, + const Real thresh, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(parts.size()), + parts(parts), + flags(flags), + phiObs(phiObs), + shift(shift), + thresh(thresh), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + BasicParticleSystem &parts, + const FlagGrid &flags, + const Grid<Real> &phiObs, + const Real shift, + const Real thresh, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + if (!parts.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) + return; + Vec3i p = toVec3i(parts.getPos(idx)); + + if (!flags.isInBounds(p)) + return; + Real v = phiObs.getInterpolated(parts.getPos(idx)); + if (v < thresh) { + Vec3 grad = getGradient(phiObs, p.x, p.y, p.z); + if (normalize(grad) < VECTOR_EPSILON) + return; + parts.setPos(idx, parts.getPos(idx) + grad * (thresh - v + shift)); + } + } + inline BasicParticleSystem &getArg0() + { + return parts; + } + typedef BasicParticleSystem type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const Grid<Real> &getArg2() + { + return phiObs; + } + typedef Grid<Real> type2; + inline const Real &getArg3() + { + return shift; + } + typedef Real type3; + inline const Real &getArg4() + { + return thresh; + } + typedef Real type4; + inline const ParticleDataImpl<int> *getArg5() + { + return ptype; + } + typedef ParticleDataImpl<int> type5; + inline const int &getArg6() + { + return exclude; + } + typedef int type6; + void runMessage() + { + debMsg("Executing kernel knPushOutofObs ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, parts, flags, phiObs, shift, thresh, ptype, exclude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystem &parts; + const FlagGrid &flags; + const Grid<Real> &phiObs; + const Real shift; + const Real thresh; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; +//! push particles out of obstacle levelset + +void pushOutofObs(BasicParticleSystem &parts, + const FlagGrid &flags, + const Grid<Real> &phiObs, + const Real shift = 0, + const Real thresh = 0, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + knPushOutofObs(parts, flags, phiObs, shift, thresh, ptype, exclude); +} +static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "pushOutofObs", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + const Grid<Real> &phiObs = *_args.getPtr<Grid<Real>>("phiObs", 2, &_lock); + const Real shift = _args.getOpt<Real>("shift", 3, 0, &_lock); + const Real thresh = _args.getOpt<Real>("thresh", 4, 0, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 5, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 6, 0, &_lock); + _retval = getPyNone(); + pushOutofObs(parts, flags, phiObs, shift, thresh, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "pushOutofObs", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("pushOutofObs", e.what()); + return 0; + } +} +static const Pb::Register _RP_pushOutofObs("", "pushOutofObs", _W_11); +extern "C" { +void PbRegister_pushOutofObs() +{ + KEEP_UNUSED(_RP_pushOutofObs); +} +} + +//****************************************************************************** +// grid interpolation functions + +template<class T> struct knSafeDivReal : public KernelBase { + knSafeDivReal(Grid<T> &me, const Grid<Real> &other, Real cutoff = VECTOR_EPSILON) + : KernelBase(&me, 0), me(me), other(other), cutoff(cutoff) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + Grid<T> &me, + const Grid<Real> &other, + Real cutoff = VECTOR_EPSILON) const + { + if (other[idx] < cutoff) { + me[idx] = 0.; + } + else { + T div(other[idx]); + me[idx] = safeDivide(me[idx], div); + } + } + inline Grid<T> &getArg0() + { + return me; + } + typedef Grid<T> type0; + inline const Grid<Real> &getArg1() + { + return other; + } + typedef Grid<Real> type1; + inline Real &getArg2() + { + return cutoff; + } + typedef Real type2; + void runMessage() + { + debMsg("Executing kernel knSafeDivReal ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, me, other, cutoff); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + Grid<T> &me; + const Grid<Real> &other; + Real cutoff; +}; + +// Set velocities on the grid from the particle system + +struct knMapLinearVec3ToMACGrid : public KernelBase { + knMapLinearVec3ToMACGrid(const BasicParticleSystem &p, + const FlagGrid &flags, + const MACGrid &vel, + Grid<Vec3> &tmp, + const ParticleDataImpl<Vec3> &pvel, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(p.size()), + p(p), + flags(flags), + vel(vel), + tmp(tmp), + pvel(pvel), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &p, + const FlagGrid &flags, + const MACGrid &vel, + Grid<Vec3> &tmp, + const ParticleDataImpl<Vec3> &pvel, + const ParticleDataImpl<int> *ptype, + const int exclude) + { + unusedParameter(flags); + if (!p.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) + return; + vel.setInterpolated(p[idx].pos, pvel[idx], &tmp[0]); + } + inline const BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const MACGrid &getArg2() + { + return vel; + } + typedef MACGrid type2; + inline Grid<Vec3> &getArg3() + { + return tmp; + } + typedef Grid<Vec3> type3; + inline const ParticleDataImpl<Vec3> &getArg4() + { + return pvel; + } + typedef ParticleDataImpl<Vec3> type4; + inline const ParticleDataImpl<int> *getArg5() + { + return ptype; + } + typedef ParticleDataImpl<int> type5; + inline const int &getArg6() + { + return exclude; + } + typedef int type6; + void runMessage() + { + debMsg("Executing kernel knMapLinearVec3ToMACGrid ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void run() + { + const IndexInt _sz = size; + for (IndexInt i = 0; i < _sz; i++) + op(i, p, flags, vel, tmp, pvel, ptype, exclude); + } + const BasicParticleSystem &p; + const FlagGrid &flags; + const MACGrid &vel; + Grid<Vec3> &tmp; + const ParticleDataImpl<Vec3> &pvel; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; + +// optionally , this function can use an existing vec3 grid to store the weights +// this is useful in combination with the simple extrapolation function + +void mapPartsToMAC(const FlagGrid &flags, + MACGrid &vel, + MACGrid &velOld, + const BasicParticleSystem &parts, + const ParticleDataImpl<Vec3> &partVel, + Grid<Vec3> *weight = NULL, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + // interpol -> grid. tmpgrid for particle contribution weights + bool freeTmp = false; + if (!weight) { + weight = new Grid<Vec3>(flags.getParent()); + freeTmp = true; + } + else { + weight->clear(); // make sure we start with a zero grid! + } + vel.clear(); + knMapLinearVec3ToMACGrid(parts, flags, vel, *weight, partVel, ptype, exclude); + + // stomp small values in weight to zero to prevent roundoff errors + weight->stomp(Vec3(VECTOR_EPSILON)); + vel.safeDivide(*weight); + + // store original state + velOld.copyFrom(vel); + if (freeTmp) + delete weight; +} +static PyObject *_W_12(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "mapPartsToMAC", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + MACGrid &velOld = *_args.getPtr<MACGrid>("velOld", 2, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 3, &_lock); + const ParticleDataImpl<Vec3> &partVel = *_args.getPtr<ParticleDataImpl<Vec3>>( + "partVel", 4, &_lock); + Grid<Vec3> *weight = _args.getPtrOpt<Grid<Vec3>>("weight", 5, NULL, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 6, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 7, 0, &_lock); + _retval = getPyNone(); + mapPartsToMAC(flags, vel, velOld, parts, partVel, weight, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "mapPartsToMAC", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("mapPartsToMAC", e.what()); + return 0; + } +} +static const Pb::Register _RP_mapPartsToMAC("", "mapPartsToMAC", _W_12); +extern "C" { +void PbRegister_mapPartsToMAC() +{ + KEEP_UNUSED(_RP_mapPartsToMAC); +} +} + +template<class T> struct knMapLinear : public KernelBase { + knMapLinear(const BasicParticleSystem &p, + const FlagGrid &flags, + const Grid<T> &target, + Grid<Real> >mp, + const ParticleDataImpl<T> &psource) + : KernelBase(p.size()), p(p), flags(flags), target(target), gtmp(gtmp), psource(psource) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &p, + const FlagGrid &flags, + const Grid<T> &target, + Grid<Real> >mp, + const ParticleDataImpl<T> &psource) + { + unusedParameter(flags); + if (!p.isActive(idx)) + return; + target.setInterpolated(p[idx].pos, psource[idx], gtmp); + } + inline const BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const Grid<T> &getArg2() + { + return target; + } + typedef Grid<T> type2; + inline Grid<Real> &getArg3() + { + return gtmp; + } + typedef Grid<Real> type3; + inline const ParticleDataImpl<T> &getArg4() + { + return psource; + } + typedef ParticleDataImpl<T> type4; + void runMessage() + { + debMsg("Executing kernel knMapLinear ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void run() + { + const IndexInt _sz = size; + for (IndexInt i = 0; i < _sz; i++) + op(i, p, flags, target, gtmp, psource); + } + const BasicParticleSystem &p; + const FlagGrid &flags; + const Grid<T> ⌖ + Grid<Real> >mp; + const ParticleDataImpl<T> &psource; +}; + +template<class T> +void mapLinearRealHelper(const FlagGrid &flags, + Grid<T> &target, + const BasicParticleSystem &parts, + const ParticleDataImpl<T> &source) +{ + Grid<Real> tmp(flags.getParent()); + target.clear(); + knMapLinear<T>(parts, flags, target, tmp, source); + knSafeDivReal<T>(target, tmp); +} + +void mapPartsToGrid(const FlagGrid &flags, + Grid<Real> &target, + const BasicParticleSystem &parts, + const ParticleDataImpl<Real> &source) +{ + mapLinearRealHelper<Real>(flags, target, parts, source); +} +static PyObject *_W_13(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "mapPartsToGrid", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &target = *_args.getPtr<Grid<Real>>("target", 1, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 2, &_lock); + const ParticleDataImpl<Real> &source = *_args.getPtr<ParticleDataImpl<Real>>( + "source", 3, &_lock); + _retval = getPyNone(); + mapPartsToGrid(flags, target, parts, source); + _args.check(); + } + pbFinalizePlugin(parent, "mapPartsToGrid", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("mapPartsToGrid", e.what()); + return 0; + } +} +static const Pb::Register _RP_mapPartsToGrid("", "mapPartsToGrid", _W_13); +extern "C" { +void PbRegister_mapPartsToGrid() +{ + KEEP_UNUSED(_RP_mapPartsToGrid); +} +} + +void mapPartsToGridVec3(const FlagGrid &flags, + Grid<Vec3> &target, + const BasicParticleSystem &parts, + const ParticleDataImpl<Vec3> &source) +{ + mapLinearRealHelper<Vec3>(flags, target, parts, source); +} +static PyObject *_W_14(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "mapPartsToGridVec3", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Vec3> &target = *_args.getPtr<Grid<Vec3>>("target", 1, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 2, &_lock); + const ParticleDataImpl<Vec3> &source = *_args.getPtr<ParticleDataImpl<Vec3>>( + "source", 3, &_lock); + _retval = getPyNone(); + mapPartsToGridVec3(flags, target, parts, source); + _args.check(); + } + pbFinalizePlugin(parent, "mapPartsToGridVec3", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("mapPartsToGridVec3", e.what()); + return 0; + } +} +static const Pb::Register _RP_mapPartsToGridVec3("", "mapPartsToGridVec3", _W_14); +extern "C" { +void PbRegister_mapPartsToGridVec3() +{ + KEEP_UNUSED(_RP_mapPartsToGridVec3); +} +} + +// integers need "max" mode, not yet implemented +// PYTHON() void mapPartsToGridInt ( FlagGrid& flags, Grid<int >& target , BasicParticleSystem& +// parts , ParticleDataImpl<int >& source ) { mapLinearRealHelper<int >(flags,target,parts,source); +//} + +template<class T> struct knMapFromGrid : public KernelBase { + knMapFromGrid(const BasicParticleSystem &p, const Grid<T> &gsrc, ParticleDataImpl<T> &target) + : KernelBase(p.size()), p(p), gsrc(gsrc), target(target) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &p, + const Grid<T> &gsrc, + ParticleDataImpl<T> &target) const + { + if (!p.isActive(idx)) + return; + target[idx] = gsrc.getInterpolated(p[idx].pos); + } + inline const BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline const Grid<T> &getArg1() + { + return gsrc; + } + typedef Grid<T> type1; + inline ParticleDataImpl<T> &getArg2() + { + return target; + } + typedef ParticleDataImpl<T> type2; + void runMessage() + { + debMsg("Executing kernel knMapFromGrid ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, p, gsrc, target); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystem &p; + const Grid<T> &gsrc; + ParticleDataImpl<T> ⌖ +}; +void mapGridToParts(const Grid<Real> &source, + const BasicParticleSystem &parts, + ParticleDataImpl<Real> &target) +{ + knMapFromGrid<Real>(parts, source, target); +} +static PyObject *_W_15(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "mapGridToParts", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<Real> &source = *_args.getPtr<Grid<Real>>("source", 0, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 1, &_lock); + ParticleDataImpl<Real> &target = *_args.getPtr<ParticleDataImpl<Real>>("target", 2, &_lock); + _retval = getPyNone(); + mapGridToParts(source, parts, target); + _args.check(); + } + pbFinalizePlugin(parent, "mapGridToParts", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("mapGridToParts", e.what()); + return 0; + } +} +static const Pb::Register _RP_mapGridToParts("", "mapGridToParts", _W_15); +extern "C" { +void PbRegister_mapGridToParts() +{ + KEEP_UNUSED(_RP_mapGridToParts); +} +} + +void mapGridToPartsVec3(const Grid<Vec3> &source, + const BasicParticleSystem &parts, + ParticleDataImpl<Vec3> &target) +{ + knMapFromGrid<Vec3>(parts, source, target); +} +static PyObject *_W_16(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "mapGridToPartsVec3", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<Vec3> &source = *_args.getPtr<Grid<Vec3>>("source", 0, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 1, &_lock); + ParticleDataImpl<Vec3> &target = *_args.getPtr<ParticleDataImpl<Vec3>>("target", 2, &_lock); + _retval = getPyNone(); + mapGridToPartsVec3(source, parts, target); + _args.check(); + } + pbFinalizePlugin(parent, "mapGridToPartsVec3", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("mapGridToPartsVec3", e.what()); + return 0; + } +} +static const Pb::Register _RP_mapGridToPartsVec3("", "mapGridToPartsVec3", _W_16); +extern "C" { +void PbRegister_mapGridToPartsVec3() +{ + KEEP_UNUSED(_RP_mapGridToPartsVec3); +} +} + +// Get velocities from grid + +struct knMapLinearMACGridToVec3_PIC : public KernelBase { + knMapLinearMACGridToVec3_PIC(const BasicParticleSystem &p, + const FlagGrid &flags, + const MACGrid &vel, + ParticleDataImpl<Vec3> &pvel, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(p.size()), + p(p), + flags(flags), + vel(vel), + pvel(pvel), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &p, + const FlagGrid &flags, + const MACGrid &vel, + ParticleDataImpl<Vec3> &pvel, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + if (!p.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) + return; + // pure PIC + pvel[idx] = vel.getInterpolated(p[idx].pos); + } + inline const BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const MACGrid &getArg2() + { + return vel; + } + typedef MACGrid type2; + inline ParticleDataImpl<Vec3> &getArg3() + { + return pvel; + } + typedef ParticleDataImpl<Vec3> type3; + inline const ParticleDataImpl<int> *getArg4() + { + return ptype; + } + typedef ParticleDataImpl<int> type4; + inline const int &getArg5() + { + return exclude; + } + typedef int type5; + void runMessage() + { + debMsg("Executing kernel knMapLinearMACGridToVec3_PIC ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, p, flags, vel, pvel, ptype, exclude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystem &p; + const FlagGrid &flags; + const MACGrid &vel; + ParticleDataImpl<Vec3> &pvel; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; + +void mapMACToParts(const FlagGrid &flags, + const MACGrid &vel, + const BasicParticleSystem &parts, + ParticleDataImpl<Vec3> &partVel, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + knMapLinearMACGridToVec3_PIC(parts, flags, vel, partVel, ptype, exclude); +} +static PyObject *_W_17(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "mapMACToParts", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 2, &_lock); + ParticleDataImpl<Vec3> &partVel = *_args.getPtr<ParticleDataImpl<Vec3>>( + "partVel", 3, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 4, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 5, 0, &_lock); + _retval = getPyNone(); + mapMACToParts(flags, vel, parts, partVel, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "mapMACToParts", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("mapMACToParts", e.what()); + return 0; + } +} +static const Pb::Register _RP_mapMACToParts("", "mapMACToParts", _W_17); +extern "C" { +void PbRegister_mapMACToParts() +{ + KEEP_UNUSED(_RP_mapMACToParts); +} +} + +// with flip delta interpolation + +struct knMapLinearMACGridToVec3_FLIP : public KernelBase { + knMapLinearMACGridToVec3_FLIP(const BasicParticleSystem &p, + const FlagGrid &flags, + const MACGrid &vel, + const MACGrid &oldVel, + ParticleDataImpl<Vec3> &pvel, + const Real flipRatio, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(p.size()), + p(p), + flags(flags), + vel(vel), + oldVel(oldVel), + pvel(pvel), + flipRatio(flipRatio), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &p, + const FlagGrid &flags, + const MACGrid &vel, + const MACGrid &oldVel, + ParticleDataImpl<Vec3> &pvel, + const Real flipRatio, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + if (!p.isActive(idx) || (ptype && ((*ptype)[idx] & exclude))) + return; + Vec3 v = vel.getInterpolated(p[idx].pos); + Vec3 delta = v - oldVel.getInterpolated(p[idx].pos); + pvel[idx] = flipRatio * (pvel[idx] + delta) + (1.0 - flipRatio) * v; + } + inline const BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const MACGrid &getArg2() + { + return vel; + } + typedef MACGrid type2; + inline const MACGrid &getArg3() + { + return oldVel; + } + typedef MACGrid type3; + inline ParticleDataImpl<Vec3> &getArg4() + { + return pvel; + } + typedef ParticleDataImpl<Vec3> type4; + inline const Real &getArg5() + { + return flipRatio; + } + typedef Real type5; + inline const ParticleDataImpl<int> *getArg6() + { + return ptype; + } + typedef ParticleDataImpl<int> type6; + inline const int &getArg7() + { + return exclude; + } + typedef int type7; + void runMessage() + { + debMsg("Executing kernel knMapLinearMACGridToVec3_FLIP ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, p, flags, vel, oldVel, pvel, flipRatio, ptype, exclude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystem &p; + const FlagGrid &flags; + const MACGrid &vel; + const MACGrid &oldVel; + ParticleDataImpl<Vec3> &pvel; + const Real flipRatio; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; + +void flipVelocityUpdate(const FlagGrid &flags, + const MACGrid &vel, + const MACGrid &velOld, + const BasicParticleSystem &parts, + ParticleDataImpl<Vec3> &partVel, + const Real flipRatio, + const ParticleDataImpl<int> *ptype = NULL, + const int exclude = 0) +{ + knMapLinearMACGridToVec3_FLIP(parts, flags, vel, velOld, partVel, flipRatio, ptype, exclude); +} +static PyObject *_W_18(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipVelocityUpdate", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const MACGrid &velOld = *_args.getPtr<MACGrid>("velOld", 2, &_lock); + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 3, &_lock); + ParticleDataImpl<Vec3> &partVel = *_args.getPtr<ParticleDataImpl<Vec3>>( + "partVel", 4, &_lock); + const Real flipRatio = _args.get<Real>("flipRatio", 5, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtrOpt<ParticleDataImpl<int>>( + "ptype", 6, NULL, &_lock); + const int exclude = _args.getOpt<int>("exclude", 7, 0, &_lock); + _retval = getPyNone(); + flipVelocityUpdate(flags, vel, velOld, parts, partVel, flipRatio, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "flipVelocityUpdate", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipVelocityUpdate", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipVelocityUpdate("", "flipVelocityUpdate", _W_18); +extern "C" { +void PbRegister_flipVelocityUpdate() +{ + KEEP_UNUSED(_RP_flipVelocityUpdate); +} +} + +//****************************************************************************** +// narrow band + +struct knCombineVels : public KernelBase { + knCombineVels(MACGrid &vel, + const Grid<Vec3> &w, + MACGrid &combineVel, + const LevelsetGrid *phi, + Real narrowBand, + Real thresh) + : KernelBase(&vel, 0), + vel(vel), + w(w), + combineVel(combineVel), + phi(phi), + narrowBand(narrowBand), + thresh(thresh) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + MACGrid &vel, + const Grid<Vec3> &w, + MACGrid &combineVel, + const LevelsetGrid *phi, + Real narrowBand, + Real thresh) const + { + int idx = vel.index(i, j, k); + + for (int c = 0; c < 3; ++c) { + // Correct narrow-band FLIP + if (phi) { + Vec3 pos(i, j, k); + pos[(c + 1) % 3] += Real(0.5); + pos[(c + 2) % 3] += Real(0.5); + Real p = phi->getInterpolated(pos); + if (p < -narrowBand) { + vel[idx][c] = 0; + continue; + } + } + + if (w[idx][c] > thresh) { + combineVel[idx][c] = vel[idx][c]; + vel[idx][c] = -1; + } + else { + vel[idx][c] = 0; + } + } + } + inline MACGrid &getArg0() + { + return vel; + } + typedef MACGrid type0; + inline const Grid<Vec3> &getArg1() + { + return w; + } + typedef Grid<Vec3> type1; + inline MACGrid &getArg2() + { + return combineVel; + } + typedef MACGrid type2; + inline const LevelsetGrid *getArg3() + { + return phi; + } + typedef LevelsetGrid type3; + inline Real &getArg4() + { + return narrowBand; + } + typedef Real type4; + inline Real &getArg5() + { + return thresh; + } + typedef Real type5; + void runMessage() + { + debMsg("Executing kernel knCombineVels ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, vel, w, combineVel, phi, narrowBand, thresh); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, vel, w, combineVel, phi, narrowBand, thresh); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + MACGrid &vel; + const Grid<Vec3> &w; + MACGrid &combineVel; + const LevelsetGrid *phi; + Real narrowBand; + Real thresh; +}; + +//! narrow band velocity combination + +void combineGridVel(MACGrid &vel, + const Grid<Vec3> &weight, + MACGrid &combineVel, + const LevelsetGrid *phi = NULL, + Real narrowBand = 0.0, + Real thresh = 0.0) +{ + knCombineVels(vel, weight, combineVel, phi, narrowBand, thresh); +} +static PyObject *_W_19(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "combineGridVel", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + const Grid<Vec3> &weight = *_args.getPtr<Grid<Vec3>>("weight", 1, &_lock); + MACGrid &combineVel = *_args.getPtr<MACGrid>("combineVel", 2, &_lock); + const LevelsetGrid *phi = _args.getPtrOpt<LevelsetGrid>("phi", 3, NULL, &_lock); + Real narrowBand = _args.getOpt<Real>("narrowBand", 4, 0.0, &_lock); + Real thresh = _args.getOpt<Real>("thresh", 5, 0.0, &_lock); + _retval = getPyNone(); + combineGridVel(vel, weight, combineVel, phi, narrowBand, thresh); + _args.check(); + } + pbFinalizePlugin(parent, "combineGridVel", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("combineGridVel", e.what()); + return 0; + } +} +static const Pb::Register _RP_combineGridVel("", "combineGridVel", _W_19); +extern "C" { +void PbRegister_combineGridVel() +{ + KEEP_UNUSED(_RP_combineGridVel); +} +} + +//! surface tension helper +void getLaplacian(Grid<Real> &laplacian, const Grid<Real> &grid) +{ + LaplaceOp(laplacian, grid); +} +static PyObject *_W_20(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "getLaplacian", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &laplacian = *_args.getPtr<Grid<Real>>("laplacian", 0, &_lock); + const Grid<Real> &grid = *_args.getPtr<Grid<Real>>("grid", 1, &_lock); + _retval = getPyNone(); + getLaplacian(laplacian, grid); + _args.check(); + } + pbFinalizePlugin(parent, "getLaplacian", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("getLaplacian", e.what()); + return 0; + } +} +static const Pb::Register _RP_getLaplacian("", "getLaplacian", _W_20); +extern "C" { +void PbRegister_getLaplacian() +{ + KEEP_UNUSED(_RP_getLaplacian); +} +} + +void getCurvature(Grid<Real> &curv, const Grid<Real> &grid, const Real h = 1.0) +{ + CurvatureOp(curv, grid, h); +} +static PyObject *_W_21(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "getCurvature", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &curv = *_args.getPtr<Grid<Real>>("curv", 0, &_lock); + const Grid<Real> &grid = *_args.getPtr<Grid<Real>>("grid", 1, &_lock); + const Real h = _args.getOpt<Real>("h", 2, 1.0, &_lock); + _retval = getPyNone(); + getCurvature(curv, grid, h); + _args.check(); + } + pbFinalizePlugin(parent, "getCurvature", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("getCurvature", e.what()); + return 0; + } +} +static const Pb::Register _RP_getCurvature("", "getCurvature", _W_21); +extern "C" { +void PbRegister_getCurvature() +{ + KEEP_UNUSED(_RP_getCurvature); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/fluidguiding.cpp b/extern/mantaflow/preprocessed/plugin/fluidguiding.cpp new file mode 100644 index 00000000000..13383581123 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/fluidguiding.cpp @@ -0,0 +1,802 @@ + + +// 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 pressure correction: solve_pressure, and ghost fluid helpers + * + ******************************************************************************/ +#include "vectorbase.h" +#include "grid.h" +#include "kernel.h" +#include "conjugategrad.h" +#include "rcmatrix.h" + +using namespace std; +namespace Manta { + +// only supports a single blur size for now, globals stored here +bool gBlurPrecomputed = false; +int gBlurKernelRadius = -1; +Matrix gBlurKernel; + +// ***************************************************************************** +// Helper functions for fluid guiding + +//! creates a 1D (horizontal) Gaussian blur kernel of size n and standard deviation sigma +Matrix get1DGaussianBlurKernel(const int n, const int sigma) +{ + Matrix x(n), y(n); + for (int j = 0; j < n; j++) { + x.add_to_element(0, j, -(n - 1) * 0.5); + y.add_to_element(0, j, j - (n - 1) * 0.5); + } + Matrix G(n); + Real sumG = 0; + for (int j = 0; j < n; j++) { + G.add_to_element(0, + j, + 1 / (2 * M_PI * sigma * sigma) * + exp(-(x(0, j) * x(0, j) + y(0, j) * y(0, j)) / (2 * sigma * sigma))); + sumG += G(0, j); + } + G = G * (1.0 / sumG); + return G; +} + +//! convolves in with 1D kernel (centred at the kernel's midpoint) in the x-direction +//! (out must be a grid of zeros) +struct apply1DKernelDirX : public KernelBase { + apply1DKernelDirX(const MACGrid &in, MACGrid &out, const Matrix &kernel) + : KernelBase(&in, 0), in(in), out(out), kernel(kernel) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const MACGrid &in, MACGrid &out, const Matrix &kernel) const + { + int nx = in.getSizeX(); + int kn = kernel.n; + int kCentre = kn / 2; + for (int m = 0, ind = kn - 1, ii = i - kCentre; m < kn; m++, ind--, ii++) { + if (ii < 0) + continue; + else if (ii >= nx) + break; + else + out(i, j, k) += in(ii, j, k) * kernel(0, ind); + } + } + inline const MACGrid &getArg0() + { + return in; + } + typedef MACGrid type0; + inline MACGrid &getArg1() + { + return out; + } + typedef MACGrid type1; + inline const Matrix &getArg2() + { + return kernel; + } + typedef Matrix type2; + void runMessage() + { + debMsg("Executing kernel apply1DKernelDirX ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, in, out, kernel); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, in, out, kernel); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const MACGrid ∈ + MACGrid &out; + const Matrix &kernel; +}; + +//! convolves in with 1D kernel (centred at the kernel's midpoint) in the y-direction +//! (out must be a grid of zeros) +struct apply1DKernelDirY : public KernelBase { + apply1DKernelDirY(const MACGrid &in, MACGrid &out, const Matrix &kernel) + : KernelBase(&in, 0), in(in), out(out), kernel(kernel) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const MACGrid &in, MACGrid &out, const Matrix &kernel) const + { + int ny = in.getSizeY(); + int kn = kernel.n; + int kCentre = kn / 2; + for (int m = 0, ind = kn - 1, jj = j - kCentre; m < kn; m++, ind--, jj++) { + if (jj < 0) + continue; + else if (jj >= ny) + break; + else + out(i, j, k) += in(i, jj, k) * kernel(0, ind); + } + } + inline const MACGrid &getArg0() + { + return in; + } + typedef MACGrid type0; + inline MACGrid &getArg1() + { + return out; + } + typedef MACGrid type1; + inline const Matrix &getArg2() + { + return kernel; + } + typedef Matrix type2; + void runMessage() + { + debMsg("Executing kernel apply1DKernelDirY ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, in, out, kernel); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, in, out, kernel); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const MACGrid ∈ + MACGrid &out; + const Matrix &kernel; +}; + +//! convolves in with 1D kernel (centred at the kernel's midpoint) in the z-direction +//! (out must be a grid of zeros) +struct apply1DKernelDirZ : public KernelBase { + apply1DKernelDirZ(const MACGrid &in, MACGrid &out, const Matrix &kernel) + : KernelBase(&in, 0), in(in), out(out), kernel(kernel) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const MACGrid &in, MACGrid &out, const Matrix &kernel) const + { + int nz = in.getSizeZ(); + int kn = kernel.n; + int kCentre = kn / 2; + for (int m = 0, ind = kn - 1, kk = k - kCentre; m < kn; m++, ind--, kk++) { + if (kk < 0) + continue; + else if (kk >= nz) + break; + else + out(i, j, k) += in(i, j, kk) * kernel(0, ind); + } + } + inline const MACGrid &getArg0() + { + return in; + } + typedef MACGrid type0; + inline MACGrid &getArg1() + { + return out; + } + typedef MACGrid type1; + inline const Matrix &getArg2() + { + return kernel; + } + typedef Matrix type2; + void runMessage() + { + debMsg("Executing kernel apply1DKernelDirZ ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, in, out, kernel); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, in, out, kernel); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const MACGrid ∈ + MACGrid &out; + const Matrix &kernel; +}; + +//! Apply separable Gaussian blur in 2D +void applySeparableKernel2D(MACGrid &grid, const FlagGrid &flags, const Matrix &kernel) +{ + // int nx = grid.getSizeX(), ny = grid.getSizeY(); + // int kn = kernel.n; + // int kCentre = kn / 2; + FluidSolver *parent = grid.getParent(); + MACGrid orig = MACGrid(parent); + orig.copyFrom(grid); + MACGrid gridX = MACGrid(parent); + apply1DKernelDirX(grid, gridX, kernel); + MACGrid gridXY = MACGrid(parent); + apply1DKernelDirY(gridX, gridXY, kernel); + grid.copyFrom(gridXY); + FOR_IJK(grid) + { + if ((i > 0 && flags.isObstacle(i - 1, j, k)) || (j > 0 && flags.isObstacle(i, j - 1, k)) || + flags.isObstacle(i, j, k)) { + grid(i, j, k).x = orig(i, j, k).x; + grid(i, j, k).y = orig(i, j, k).y; + grid(i, j, k).z = orig(i, j, k).z; + } + } +} + +//! Apply separable Gaussian blur in 3D +void applySeparableKernel3D(MACGrid &grid, const FlagGrid &flags, const Matrix &kernel) +{ + // int nx = grid.getSizeX(), ny = grid.getSizeY(), nz = grid.getSizeZ(); + // int kn = kernel.n; + // int kCentre = kn / 2; + FluidSolver *parent = grid.getParent(); + MACGrid orig = MACGrid(parent); + orig.copyFrom(grid); + MACGrid gridX = MACGrid(parent); + apply1DKernelDirX(grid, gridX, kernel); + MACGrid gridXY = MACGrid(parent); + apply1DKernelDirY(gridX, gridXY, kernel); + MACGrid gridXYZ = MACGrid(parent); + apply1DKernelDirZ(gridXY, gridXYZ, kernel); + grid.copyFrom(gridXYZ); + FOR_IJK(grid) + { + if ((i > 0 && flags.isObstacle(i - 1, j, k)) || (j > 0 && flags.isObstacle(i, j - 1, k)) || + (k > 0 && flags.isObstacle(i, j, k - 1)) || flags.isObstacle(i, j, k)) { + grid(i, j, k).x = orig(i, j, k).x; + grid(i, j, k).y = orig(i, j, k).y; + grid(i, j, k).z = orig(i, j, k).z; + } + } +} + +//! Apply separable Gaussian blur in 2D or 3D depending on input dimensions +void applySeparableKernel(MACGrid &grid, const FlagGrid &flags, const Matrix &kernel) +{ + if (!grid.is3D()) + applySeparableKernel2D(grid, flags, kernel); + else + applySeparableKernel3D(grid, flags, kernel); +} + +//! Compute r-norm for the stopping criterion +Real getRNorm(const MACGrid &x, const MACGrid &z) +{ + MACGrid r = MACGrid(x.getParent()); + r.copyFrom(x); + r.sub(z); + return r.getMaxAbs(); +} + +//! Compute s-norm for the stopping criterion +Real getSNorm(const Real rho, const MACGrid &z, const MACGrid &z_prev) +{ + MACGrid s = MACGrid(z_prev.getParent()); + s.copyFrom(z_prev); + s.sub(z); + s.multConst(rho); + return s.getMaxAbs(); +} + +//! Compute primal eps for the stopping criterion +Real getEpsPri(const Real eps_abs, const Real eps_rel, const MACGrid &x, const MACGrid &z) +{ + Real max_norm = max(x.getMaxAbs(), z.getMaxAbs()); + Real eps_pri = sqrt(x.is3D() ? 3.0 : 2.0) * eps_abs + eps_rel * max_norm; + return eps_pri; +} + +//! Compute dual eps for the stopping criterion +Real getEpsDual(const Real eps_abs, const Real eps_rel, const MACGrid &y) +{ + Real eps_dual = sqrt(y.is3D() ? 3.0 : 2.0) * eps_abs + eps_rel * y.getMaxAbs(); + return eps_dual; +} + +//! Create a spiral velocity field in 2D as a test scene (optionally in 3D) +void getSpiralVelocity(const FlagGrid &flags, + MACGrid &vel, + Real strength = 1.0, + bool with3D = false) +{ + int nx = flags.getSizeX(), ny = flags.getSizeY(), nz = 1; + if (with3D) + nz = flags.getSizeZ(); + Real midX = 0.5 * (Real)(nx - 1); + Real midY = 0.5 * (Real)(ny - 1); + Real midZ = 0.5 * (Real)(nz - 1); + for (int i = 0; i < nx; i++) { + for (int j = 0; j < ny; j++) { + for (int k = 0; k < nz; k++) { + int idx = flags.index(i, j, k); + Real diffX = midX - i; + Real diffY = midY - j; + Real hypotenuse = sqrt(diffX * diffX + diffY * diffY); + if (hypotenuse > 0) { + vel[idx].x = diffY / hypotenuse; + vel[idx].y = -diffX / hypotenuse; + } + } + } + } + vel.multConst(strength); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "getSpiralVelocity", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + Real strength = _args.getOpt<Real>("strength", 2, 1.0, &_lock); + bool with3D = _args.getOpt<bool>("with3D", 3, false, &_lock); + _retval = getPyNone(); + getSpiralVelocity(flags, vel, strength, with3D); + _args.check(); + } + pbFinalizePlugin(parent, "getSpiralVelocity", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("getSpiralVelocity", e.what()); + return 0; + } +} +static const Pb::Register _RP_getSpiralVelocity("", "getSpiralVelocity", _W_0); +extern "C" { +void PbRegister_getSpiralVelocity() +{ + KEEP_UNUSED(_RP_getSpiralVelocity); +} +} + +//! Set the guiding weight W as a gradient in the y-direction +void setGradientYWeight( + Grid<Real> &W, const int minY, const int maxY, const Real valAtMin, const Real valAtMax) +{ + FOR_IJK(W) + { + if (minY <= j && j <= maxY) { + Real val = valAtMin; + if (valAtMax != valAtMin) { + Real ratio = (Real)(j - minY) / (Real)(maxY - minY); + val = ratio * valAtMax + (1.0 - ratio) * valAtMin; + } + W(i, j, k) = val; + } + } +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setGradientYWeight", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &W = *_args.getPtr<Grid<Real>>("W", 0, &_lock); + const int minY = _args.get<int>("minY", 1, &_lock); + const int maxY = _args.get<int>("maxY", 2, &_lock); + const Real valAtMin = _args.get<Real>("valAtMin", 3, &_lock); + const Real valAtMax = _args.get<Real>("valAtMax", 4, &_lock); + _retval = getPyNone(); + setGradientYWeight(W, minY, maxY, valAtMin, valAtMax); + _args.check(); + } + pbFinalizePlugin(parent, "setGradientYWeight", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setGradientYWeight", e.what()); + return 0; + } +} +static const Pb::Register _RP_setGradientYWeight("", "setGradientYWeight", _W_1); +extern "C" { +void PbRegister_setGradientYWeight() +{ + KEEP_UNUSED(_RP_setGradientYWeight); +} +} + +// ***************************************************************************** +// More helper functions for fluid guiding + +//! Apply Gaussian blur (either 2D or 3D) in a separable way +void applySeparableGaussianBlur(MACGrid &grid, const FlagGrid &flags, const Matrix &kernel1D) +{ + assertMsg(gBlurPrecomputed, "Error - blue kernel not precomputed"); + applySeparableKernel(grid, flags, kernel1D); +} + +//! Precomputation performed before the first PD iteration +void ADMM_precompute_Separable(int blurRadius) +{ + if (gBlurPrecomputed) { + assertMsg(gBlurKernelRadius == blurRadius, + "More than a single blur radius not supported at the moment."); + return; + } + int kernelSize = 2 * blurRadius + 1; + gBlurKernel = get1DGaussianBlurKernel(kernelSize, kernelSize); + gBlurPrecomputed = true; + gBlurKernelRadius = blurRadius; +} + +//! Apply approximate multiplication of inverse(M) +void applyApproxInvM(MACGrid &v, const FlagGrid &flags, const MACGrid &invA) +{ + MACGrid v_new = MACGrid(v.getParent()); + v_new.copyFrom(v); + v_new.mult(invA); + applySeparableGaussianBlur(v_new, flags, gBlurKernel); + applySeparableGaussianBlur(v_new, flags, gBlurKernel); + v_new.multConst(2.0); + v_new.mult(invA); + v.mult(invA); + v.sub(v_new); +} + +//! Precompute Q, a reused quantity in the PD iterations +//! Q = 2*G*G*(velT-velC)-sigma*velC +void precomputeQ(MACGrid &Q, + const FlagGrid &flags, + const MACGrid &velT_region, + const MACGrid &velC, + const Matrix &gBlurKernel, + const Real sigma) +{ + Q.copyFrom(velT_region); + Q.sub(velC); + applySeparableGaussianBlur(Q, flags, gBlurKernel); + applySeparableGaussianBlur(Q, flags, gBlurKernel); + Q.multConst(2.0); + Q.addScaled(velC, -sigma); +} + +//! Precompute inverse(A), a reused quantity in the PD iterations +//! A = 2*S^2 + p*I, invA = elementwise 1/A +void precomputeInvA(MACGrid &invA, const Grid<Real> &weight, const Real sigma) +{ + FOR_IJK(invA) + { + Real val = 2 * weight(i, j, k) * weight(i, j, k) + sigma; + if (val < 0.01) + val = 0.01; + Real invVal = 1.0 / val; + invA(i, j, k).x = invVal; + invA(i, j, k).y = invVal; + invA(i, j, k).z = invVal; + } +} + +//! proximal operator of f , guiding +void prox_f(MACGrid &v, + const FlagGrid &flags, + const MACGrid &Q, + const MACGrid &velC, + const Real sigma, + const MACGrid &invA) +{ + v.multConst(sigma); + v.add(Q); + applyApproxInvM(v, flags, invA); + v.add(velC); +} + +// ***************************************************************************** + +// re-uses main pressure solve from pressure.cpp +void solvePressure(MACGrid &vel, + Grid<Real> &pressure, + const FlagGrid &flags, + Real cgAccuracy = 1e-3, + const Grid<Real> *phi = 0, + const Grid<Real> *perCellCorr = 0, + const MACGrid *fractions = 0, + const MACGrid *obvel = 0, + Real gfClamp = 1e-04, + Real cgMaxIterFac = 1.5, + bool precondition = true, + int preconditioner = 1, + bool enforceCompatibility = false, + bool useL2Norm = false, + bool zeroPressureFixing = false, + const Grid<Real> *curv = NULL, + const Real surfTens = 0.0, + Grid<Real> *retRhs = NULL); + +//! Main function for fluid guiding , includes "regular" pressure solve + +void PD_fluid_guiding(MACGrid &vel, + MACGrid &velT, + Grid<Real> &pressure, + FlagGrid &flags, + Grid<Real> &weight, + int blurRadius = 5, + Real theta = 1.0, + Real tau = 1.0, + Real sigma = 1.0, + Real epsRel = 1e-3, + Real epsAbs = 1e-3, + int maxIters = 200, + Grid<Real> *phi = 0, + Grid<Real> *perCellCorr = 0, + MACGrid *fractions = 0, + MACGrid *obvel = 0, + Real gfClamp = 1e-04, + Real cgMaxIterFac = 1.5, + Real cgAccuracy = 1e-3, + int preconditioner = 1, + bool zeroPressureFixing = false, + const Grid<Real> *curv = NULL, + const Real surfTens = 0.) +{ + FluidSolver *parent = vel.getParent(); + + // initialize dual/slack variables + MACGrid velC = MACGrid(parent); + velC.copyFrom(vel); + MACGrid x = MACGrid(parent); + MACGrid y = MACGrid(parent); + MACGrid z = MACGrid(parent); + MACGrid x0 = MACGrid(parent); + MACGrid z0 = MACGrid(parent); + + // precomputation + ADMM_precompute_Separable(blurRadius); + MACGrid Q = MACGrid(parent); + precomputeQ(Q, flags, velT, velC, gBlurKernel, sigma); + MACGrid invA = MACGrid(parent); + precomputeInvA(invA, weight, sigma); + + // loop + int iter = 0; + for (iter = 0; iter < maxIters; iter++) { + // x-update + x0.copyFrom(x); + x.multConst(1.0 / sigma); + x.add(y); + prox_f(x, flags, Q, velC, sigma, invA); + x.multConst(-sigma); + x.addScaled(y, sigma); + x.add(x0); + + // z-update + z0.copyFrom(z); + z.addScaled(x, -tau); + Real cgAccuracyAdaptive = cgAccuracy; + + solvePressure(z, + pressure, + flags, + cgAccuracyAdaptive, + phi, + perCellCorr, + fractions, + obvel, + gfClamp, + cgMaxIterFac, + true, + preconditioner, + false, + false, + zeroPressureFixing, + curv, + surfTens); + + // y-update + y.copyFrom(z); + y.sub(z0); + y.multConst(theta); + y.add(z); + + // stopping criterion + bool stop = (iter > 0 && getRNorm(z, z0) < getEpsDual(epsAbs, epsRel, z)); + + if (stop || (iter == maxIters - 1)) + break; + } + + // vel_new = z + vel.copyFrom(z); + + debMsg("PD_fluid_guiding iterations:" << iter, 1); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "PD_fluid_guiding", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + MACGrid &velT = *_args.getPtr<MACGrid>("velT", 1, &_lock); + Grid<Real> &pressure = *_args.getPtr<Grid<Real>>("pressure", 2, &_lock); + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 3, &_lock); + Grid<Real> &weight = *_args.getPtr<Grid<Real>>("weight", 4, &_lock); + int blurRadius = _args.getOpt<int>("blurRadius", 5, 5, &_lock); + Real theta = _args.getOpt<Real>("theta", 6, 1.0, &_lock); + Real tau = _args.getOpt<Real>("tau", 7, 1.0, &_lock); + Real sigma = _args.getOpt<Real>("sigma", 8, 1.0, &_lock); + Real epsRel = _args.getOpt<Real>("epsRel", 9, 1e-3, &_lock); + Real epsAbs = _args.getOpt<Real>("epsAbs", 10, 1e-3, &_lock); + int maxIters = _args.getOpt<int>("maxIters", 11, 200, &_lock); + Grid<Real> *phi = _args.getPtrOpt<Grid<Real>>("phi", 12, 0, &_lock); + Grid<Real> *perCellCorr = _args.getPtrOpt<Grid<Real>>("perCellCorr", 13, 0, &_lock); + MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 14, 0, &_lock); + MACGrid *obvel = _args.getPtrOpt<MACGrid>("obvel", 15, 0, &_lock); + Real gfClamp = _args.getOpt<Real>("gfClamp", 16, 1e-04, &_lock); + Real cgMaxIterFac = _args.getOpt<Real>("cgMaxIterFac", 17, 1.5, &_lock); + Real cgAccuracy = _args.getOpt<Real>("cgAccuracy", 18, 1e-3, &_lock); + int preconditioner = _args.getOpt<int>("preconditioner", 19, 1, &_lock); + bool zeroPressureFixing = _args.getOpt<bool>("zeroPressureFixing", 20, false, &_lock); + const Grid<Real> *curv = _args.getPtrOpt<Grid<Real>>("curv", 21, NULL, &_lock); + const Real surfTens = _args.getOpt<Real>("surfTens", 22, 0., &_lock); + _retval = getPyNone(); + PD_fluid_guiding(vel, + velT, + pressure, + flags, + weight, + blurRadius, + theta, + tau, + sigma, + epsRel, + epsAbs, + maxIters, + phi, + perCellCorr, + fractions, + obvel, + gfClamp, + cgMaxIterFac, + cgAccuracy, + preconditioner, + zeroPressureFixing, + curv, + surfTens); + _args.check(); + } + pbFinalizePlugin(parent, "PD_fluid_guiding", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("PD_fluid_guiding", e.what()); + return 0; + } +} +static const Pb::Register _RP_PD_fluid_guiding("", "PD_fluid_guiding", _W_2); +extern "C" { +void PbRegister_PD_fluid_guiding() +{ + KEEP_UNUSED(_RP_PD_fluid_guiding); +} +} + +//! reset precomputation +void releaseBlurPrecomp() +{ + gBlurPrecomputed = false; + gBlurKernelRadius = -1; + gBlurKernel = 0.f; +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "releaseBlurPrecomp", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + _retval = getPyNone(); + releaseBlurPrecomp(); + _args.check(); + } + pbFinalizePlugin(parent, "releaseBlurPrecomp", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("releaseBlurPrecomp", e.what()); + return 0; + } +} +static const Pb::Register _RP_releaseBlurPrecomp("", "releaseBlurPrecomp", _W_3); +extern "C" { +void PbRegister_releaseBlurPrecomp() +{ + KEEP_UNUSED(_RP_releaseBlurPrecomp); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/initplugins.cpp b/extern/mantaflow/preprocessed/plugin/initplugins.cpp new file mode 100644 index 00000000000..3e28c947424 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/initplugins.cpp @@ -0,0 +1,2317 @@ + + +// 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 + * + * Tools to setup fields and inflows + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "shapes.h" +#include "commonkernels.h" +#include "particle.h" +#include "noisefield.h" +#include "simpleimage.h" +#include "mesh.h" + +using namespace std; + +namespace Manta { + +//! Apply noise to grid + +struct KnApplyNoiseInfl : public KernelBase { + KnApplyNoiseInfl(const FlagGrid &flags, + Grid<Real> &density, + const WaveletNoiseField &noise, + const Grid<Real> &sdf, + Real scale, + Real sigma) + : KernelBase(&flags, 0), + flags(flags), + density(density), + noise(noise), + sdf(sdf), + scale(scale), + sigma(sigma) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &density, + const WaveletNoiseField &noise, + const Grid<Real> &sdf, + Real scale, + Real sigma) const + { + if (!flags.isFluid(i, j, k) || sdf(i, j, k) > sigma) + return; + Real factor = clamp(1.0 - 0.5 / sigma * (sdf(i, j, k) + sigma), 0.0, 1.0); + + Real target = noise.evaluate(Vec3(i, j, k)) * scale * factor; + if (density(i, j, k) < target) + density(i, j, k) = target; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return density; + } + typedef Grid<Real> type1; + inline const WaveletNoiseField &getArg2() + { + return noise; + } + typedef WaveletNoiseField type2; + inline const Grid<Real> &getArg3() + { + return sdf; + } + typedef Grid<Real> type3; + inline Real &getArg4() + { + return scale; + } + typedef Real type4; + inline Real &getArg5() + { + return sigma; + } + typedef Real type5; + void runMessage() + { + debMsg("Executing kernel KnApplyNoiseInfl ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, density, noise, sdf, scale, sigma); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, density, noise, sdf, scale, sigma); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> &density; + const WaveletNoiseField &noise; + const Grid<Real> &sdf; + Real scale; + Real sigma; +}; + +//! Init noise-modulated density inside shape + +void densityInflow(const FlagGrid &flags, + Grid<Real> &density, + const WaveletNoiseField &noise, + Shape *shape, + Real scale = 1.0, + Real sigma = 0) +{ + Grid<Real> sdf = shape->computeLevelset(); + KnApplyNoiseInfl(flags, density, noise, sdf, scale, sigma); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "densityInflow", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Shape *shape = _args.getPtr<Shape>("shape", 3, &_lock); + Real scale = _args.getOpt<Real>("scale", 4, 1.0, &_lock); + Real sigma = _args.getOpt<Real>("sigma", 5, 0, &_lock); + _retval = getPyNone(); + densityInflow(flags, density, noise, shape, scale, sigma); + _args.check(); + } + pbFinalizePlugin(parent, "densityInflow", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("densityInflow", e.what()); + return 0; + } +} +static const Pb::Register _RP_densityInflow("", "densityInflow", _W_0); +extern "C" { +void PbRegister_densityInflow() +{ + KEEP_UNUSED(_RP_densityInflow); +} +} + +//! Apply noise to real grid based on an SDF +struct KnAddNoise : public KernelBase { + KnAddNoise(const FlagGrid &flags, + Grid<Real> &density, + const WaveletNoiseField &noise, + const Grid<Real> *sdf, + Real scale) + : KernelBase(&flags, 0), flags(flags), density(density), noise(noise), sdf(sdf), scale(scale) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &density, + const WaveletNoiseField &noise, + const Grid<Real> *sdf, + Real scale) const + { + if (!flags.isFluid(i, j, k) || (sdf && (*sdf)(i, j, k) > 0.)) + return; + density(i, j, k) += noise.evaluate(Vec3(i, j, k)) * scale; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return density; + } + typedef Grid<Real> type1; + inline const WaveletNoiseField &getArg2() + { + return noise; + } + typedef WaveletNoiseField type2; + inline const Grid<Real> *getArg3() + { + return sdf; + } + typedef Grid<Real> type3; + inline Real &getArg4() + { + return scale; + } + typedef Real type4; + void runMessage() + { + debMsg("Executing kernel KnAddNoise ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, density, noise, sdf, scale); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, density, noise, sdf, scale); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> &density; + const WaveletNoiseField &noise; + const Grid<Real> *sdf; + Real scale; +}; +void addNoise(const FlagGrid &flags, + Grid<Real> &density, + const WaveletNoiseField &noise, + const Grid<Real> *sdf = NULL, + Real scale = 1.0) +{ + KnAddNoise(flags, density, noise, sdf, scale); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "addNoise", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + const Grid<Real> *sdf = _args.getPtrOpt<Grid<Real>>("sdf", 3, NULL, &_lock); + Real scale = _args.getOpt<Real>("scale", 4, 1.0, &_lock); + _retval = getPyNone(); + addNoise(flags, density, noise, sdf, scale); + _args.check(); + } + pbFinalizePlugin(parent, "addNoise", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("addNoise", e.what()); + return 0; + } +} +static const Pb::Register _RP_addNoise("", "addNoise", _W_1); +extern "C" { +void PbRegister_addNoise() +{ + KEEP_UNUSED(_RP_addNoise); +} +} + +//! sample noise field and set pdata with its values (for convenience, scale the noise values) + +template<class T> struct knSetPdataNoise : public KernelBase { + knSetPdataNoise(const BasicParticleSystem &parts, + ParticleDataImpl<T> &pdata, + const WaveletNoiseField &noise, + Real scale) + : KernelBase(parts.size()), parts(parts), pdata(pdata), noise(noise), scale(scale) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &parts, + ParticleDataImpl<T> &pdata, + const WaveletNoiseField &noise, + Real scale) const + { + pdata[idx] = noise.evaluate(parts.getPos(idx)) * scale; + } + inline const BasicParticleSystem &getArg0() + { + return parts; + } + typedef BasicParticleSystem type0; + inline ParticleDataImpl<T> &getArg1() + { + return pdata; + } + typedef ParticleDataImpl<T> type1; + inline const WaveletNoiseField &getArg2() + { + return noise; + } + typedef WaveletNoiseField type2; + inline Real &getArg3() + { + return scale; + } + typedef Real type3; + void runMessage() + { + debMsg("Executing kernel knSetPdataNoise ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, parts, pdata, noise, scale); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystem &parts; + ParticleDataImpl<T> &pdata; + const WaveletNoiseField &noise; + Real scale; +}; + +template<class T> struct knSetPdataNoiseVec : public KernelBase { + knSetPdataNoiseVec(const BasicParticleSystem &parts, + ParticleDataImpl<T> &pdata, + const WaveletNoiseField &noise, + Real scale) + : KernelBase(parts.size()), parts(parts), pdata(pdata), noise(noise), scale(scale) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &parts, + ParticleDataImpl<T> &pdata, + const WaveletNoiseField &noise, + Real scale) const + { + pdata[idx] = noise.evaluateVec(parts.getPos(idx)) * scale; + } + inline const BasicParticleSystem &getArg0() + { + return parts; + } + typedef BasicParticleSystem type0; + inline ParticleDataImpl<T> &getArg1() + { + return pdata; + } + typedef ParticleDataImpl<T> type1; + inline const WaveletNoiseField &getArg2() + { + return noise; + } + typedef WaveletNoiseField type2; + inline Real &getArg3() + { + return scale; + } + typedef Real type3; + void runMessage() + { + debMsg("Executing kernel knSetPdataNoiseVec ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, parts, pdata, noise, scale); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystem &parts; + ParticleDataImpl<T> &pdata; + const WaveletNoiseField &noise; + Real scale; +}; +void setNoisePdata(const BasicParticleSystem &parts, + ParticleDataImpl<Real> &pd, + const WaveletNoiseField &noise, + Real scale = 1.) +{ + knSetPdataNoise<Real>(parts, pd, noise, scale); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setNoisePdata", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + ParticleDataImpl<Real> &pd = *_args.getPtr<ParticleDataImpl<Real>>("pd", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Real scale = _args.getOpt<Real>("scale", 3, 1., &_lock); + _retval = getPyNone(); + setNoisePdata(parts, pd, noise, scale); + _args.check(); + } + pbFinalizePlugin(parent, "setNoisePdata", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setNoisePdata", e.what()); + return 0; + } +} +static const Pb::Register _RP_setNoisePdata("", "setNoisePdata", _W_2); +extern "C" { +void PbRegister_setNoisePdata() +{ + KEEP_UNUSED(_RP_setNoisePdata); +} +} + +void setNoisePdataVec3(const BasicParticleSystem &parts, + ParticleDataImpl<Vec3> &pd, + const WaveletNoiseField &noise, + Real scale = 1.) +{ + knSetPdataNoiseVec<Vec3>(parts, pd, noise, scale); +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setNoisePdataVec3", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + ParticleDataImpl<Vec3> &pd = *_args.getPtr<ParticleDataImpl<Vec3>>("pd", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Real scale = _args.getOpt<Real>("scale", 3, 1., &_lock); + _retval = getPyNone(); + setNoisePdataVec3(parts, pd, noise, scale); + _args.check(); + } + pbFinalizePlugin(parent, "setNoisePdataVec3", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setNoisePdataVec3", e.what()); + return 0; + } +} +static const Pb::Register _RP_setNoisePdataVec3("", "setNoisePdataVec3", _W_3); +extern "C" { +void PbRegister_setNoisePdataVec3() +{ + KEEP_UNUSED(_RP_setNoisePdataVec3); +} +} + +void setNoisePdataInt(const BasicParticleSystem &parts, + ParticleDataImpl<int> &pd, + const WaveletNoiseField &noise, + Real scale = 1.) +{ + knSetPdataNoise<int>(parts, pd, noise, scale); +} +static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setNoisePdataInt", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + ParticleDataImpl<int> &pd = *_args.getPtr<ParticleDataImpl<int>>("pd", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Real scale = _args.getOpt<Real>("scale", 3, 1., &_lock); + _retval = getPyNone(); + setNoisePdataInt(parts, pd, noise, scale); + _args.check(); + } + pbFinalizePlugin(parent, "setNoisePdataInt", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setNoisePdataInt", e.what()); + return 0; + } +} +static const Pb::Register _RP_setNoisePdataInt("", "setNoisePdataInt", _W_4); +extern "C" { +void PbRegister_setNoisePdataInt() +{ + KEEP_UNUSED(_RP_setNoisePdataInt); +} +} + +//! SDF gradient from obstacle flags, for turbulence.py +// FIXME, slow, without kernel... +Grid<Vec3> obstacleGradient(const FlagGrid &flags) +{ + LevelsetGrid levelset(flags.getParent(), false); + Grid<Vec3> gradient(flags.getParent()); + + // rebuild obstacle levelset + FOR_IDX(levelset) + { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + // build levelset gradient + GradientOp(gradient, levelset); + + FOR_IDX(levelset) + { + Vec3 grad = gradient[idx]; + Real s = normalize(grad); + if (s <= 0.1 || levelset[idx] >= 0) + grad = Vec3(0.); + gradient[idx] = grad * levelset[idx]; + } + + return gradient; +} +static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "obstacleGradient", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + _retval = toPy(obstacleGradient(flags)); + _args.check(); + } + pbFinalizePlugin(parent, "obstacleGradient", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("obstacleGradient", e.what()); + return 0; + } +} +static const Pb::Register _RP_obstacleGradient("", "obstacleGradient", _W_5); +extern "C" { +void PbRegister_obstacleGradient() +{ + KEEP_UNUSED(_RP_obstacleGradient); +} +} + +//! SDF from obstacle flags, for turbulence.py +LevelsetGrid obstacleLevelset(const FlagGrid &flags) +{ + LevelsetGrid levelset(flags.getParent(), false); + + // rebuild obstacle levelset + FOR_IDX(levelset) + { + levelset[idx] = flags.isObstacle(idx) ? -0.5 : 0.5; + } + levelset.reinitMarching(flags, 6.0, 0, true, false, FlagGrid::TypeReserved); + + return levelset; +} +static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "obstacleLevelset", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + _retval = toPy(obstacleLevelset(flags)); + _args.check(); + } + pbFinalizePlugin(parent, "obstacleLevelset", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("obstacleLevelset", e.what()); + return 0; + } +} +static const Pb::Register _RP_obstacleLevelset("", "obstacleLevelset", _W_6); +extern "C" { +void PbRegister_obstacleLevelset() +{ + KEEP_UNUSED(_RP_obstacleLevelset); +} +} + +//***************************************************************************** +// blender init functions + +struct KnApplyEmission : public KernelBase { + KnApplyEmission(const FlagGrid &flags, + Grid<Real> &target, + const Grid<Real> &source, + const Grid<Real> *emissionTexture, + bool isAbsolute, + int type) + : KernelBase(&flags, 0), + flags(flags), + target(target), + source(source), + emissionTexture(emissionTexture), + isAbsolute(isAbsolute), + type(type) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &target, + const Grid<Real> &source, + const Grid<Real> *emissionTexture, + bool isAbsolute, + int type) const + { + // if type is given, only apply emission when celltype matches type from flaggrid + // and if emission texture is given, only apply emission when some emission is present at cell + // (important for emit from particles) + bool isInflow = (type & FlagGrid::TypeInflow && flags.isInflow(i, j, k)); + bool isOutflow = (type & FlagGrid::TypeOutflow && flags.isOutflow(i, j, k)); + if ((type && !isInflow && !isOutflow) && (emissionTexture && !(*emissionTexture)(i, j, k))) + return; + + if (isAbsolute) + target(i, j, k) = source(i, j, k); + else + target(i, j, k) += source(i, j, k); + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return target; + } + typedef Grid<Real> type1; + inline const Grid<Real> &getArg2() + { + return source; + } + typedef Grid<Real> type2; + inline const Grid<Real> *getArg3() + { + return emissionTexture; + } + typedef Grid<Real> type3; + inline bool &getArg4() + { + return isAbsolute; + } + typedef bool type4; + inline int &getArg5() + { + return type; + } + typedef int type5; + void runMessage() + { + debMsg("Executing kernel KnApplyEmission ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, target, source, emissionTexture, isAbsolute, type); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, target, source, emissionTexture, isAbsolute, type); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> ⌖ + const Grid<Real> &source; + const Grid<Real> *emissionTexture; + bool isAbsolute; + int type; +}; + +//! Add emission values +// isAbsolute: whether to add emission values to existing, or replace +void applyEmission(FlagGrid &flags, + Grid<Real> &target, + Grid<Real> &source, + Grid<Real> *emissionTexture = NULL, + bool isAbsolute = true, + int type = 0) +{ + KnApplyEmission(flags, target, source, emissionTexture, isAbsolute, type); +} +static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "applyEmission", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &target = *_args.getPtr<Grid<Real>>("target", 1, &_lock); + Grid<Real> &source = *_args.getPtr<Grid<Real>>("source", 2, &_lock); + Grid<Real> *emissionTexture = _args.getPtrOpt<Grid<Real>>( + "emissionTexture", 3, NULL, &_lock); + bool isAbsolute = _args.getOpt<bool>("isAbsolute", 4, true, &_lock); + int type = _args.getOpt<int>("type", 5, 0, &_lock); + _retval = getPyNone(); + applyEmission(flags, target, source, emissionTexture, isAbsolute, type); + _args.check(); + } + pbFinalizePlugin(parent, "applyEmission", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("applyEmission", e.what()); + return 0; + } +} +static const Pb::Register _RP_applyEmission("", "applyEmission", _W_7); +extern "C" { +void PbRegister_applyEmission() +{ + KEEP_UNUSED(_RP_applyEmission); +} +} + +// blender init functions for meshes + +struct KnApplyDensity : public KernelBase { + KnApplyDensity( + const FlagGrid &flags, Grid<Real> &density, const Grid<Real> &sdf, Real value, Real sigma) + : KernelBase(&flags, 0), flags(flags), density(density), sdf(sdf), value(value), sigma(sigma) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &density, + const Grid<Real> &sdf, + Real value, + Real sigma) const + { + if (!flags.isFluid(i, j, k) || sdf(i, j, k) > sigma) + return; + density(i, j, k) = value; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return density; + } + typedef Grid<Real> type1; + inline const Grid<Real> &getArg2() + { + return sdf; + } + typedef Grid<Real> type2; + inline Real &getArg3() + { + return value; + } + typedef Real type3; + inline Real &getArg4() + { + return sigma; + } + typedef Real type4; + void runMessage() + { + debMsg("Executing kernel KnApplyDensity ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, density, sdf, value, sigma); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, density, sdf, value, sigma); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> &density; + const Grid<Real> &sdf; + Real value; + Real sigma; +}; +//! Init noise-modulated density inside mesh + +void densityInflowMeshNoise(const FlagGrid &flags, + Grid<Real> &density, + const WaveletNoiseField &noise, + Mesh *mesh, + Real scale = 1.0, + Real sigma = 0) +{ + LevelsetGrid sdf(density.getParent(), false); + mesh->computeLevelset(sdf, 1.); + KnApplyNoiseInfl(flags, density, noise, sdf, scale, sigma); +} +static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "densityInflowMeshNoise", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Mesh *mesh = _args.getPtr<Mesh>("mesh", 3, &_lock); + Real scale = _args.getOpt<Real>("scale", 4, 1.0, &_lock); + Real sigma = _args.getOpt<Real>("sigma", 5, 0, &_lock); + _retval = getPyNone(); + densityInflowMeshNoise(flags, density, noise, mesh, scale, sigma); + _args.check(); + } + pbFinalizePlugin(parent, "densityInflowMeshNoise", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("densityInflowMeshNoise", e.what()); + return 0; + } +} +static const Pb::Register _RP_densityInflowMeshNoise("", "densityInflowMeshNoise", _W_8); +extern "C" { +void PbRegister_densityInflowMeshNoise() +{ + KEEP_UNUSED(_RP_densityInflowMeshNoise); +} +} + +//! Init constant density inside mesh + +void densityInflowMesh(const FlagGrid &flags, + Grid<Real> &density, + Mesh *mesh, + Real value = 1., + Real cutoff = 7, + Real sigma = 0) +{ + LevelsetGrid sdf(density.getParent(), false); + mesh->computeLevelset(sdf, 2., cutoff); + KnApplyDensity(flags, density, sdf, value, sigma); +} +static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "densityInflowMesh", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + Mesh *mesh = _args.getPtr<Mesh>("mesh", 2, &_lock); + Real value = _args.getOpt<Real>("value", 3, 1., &_lock); + Real cutoff = _args.getOpt<Real>("cutoff", 4, 7, &_lock); + Real sigma = _args.getOpt<Real>("sigma", 5, 0, &_lock); + _retval = getPyNone(); + densityInflowMesh(flags, density, mesh, value, cutoff, sigma); + _args.check(); + } + pbFinalizePlugin(parent, "densityInflowMesh", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("densityInflowMesh", e.what()); + return 0; + } +} +static const Pb::Register _RP_densityInflowMesh("", "densityInflowMesh", _W_9); +extern "C" { +void PbRegister_densityInflowMesh() +{ + KEEP_UNUSED(_RP_densityInflowMesh); +} +} + +//***************************************************************************** + +//! check for symmetry , optionally enfore by copying + +void checkSymmetry( + Grid<Real> &a, Grid<Real> *err = NULL, bool symmetrize = false, int axis = 0, int bound = 0) +{ + const int c = axis; + const int s = a.getSize()[c]; + FOR_IJK(a) + { + Vec3i idx(i, j, k), mdx(i, j, k); + mdx[c] = s - 1 - idx[c]; + if (bound > 0 && ((!a.isInBounds(idx, bound)) || (!a.isInBounds(mdx, bound)))) + continue; + + if (err) + (*err)(idx) = fabs((double)(a(idx) - a(mdx))); + if (symmetrize && (idx[c] < s / 2)) { + a(idx) = a(mdx); + } + } +} +static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "checkSymmetry", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &a = *_args.getPtr<Grid<Real>>("a", 0, &_lock); + Grid<Real> *err = _args.getPtrOpt<Grid<Real>>("err", 1, NULL, &_lock); + bool symmetrize = _args.getOpt<bool>("symmetrize", 2, false, &_lock); + int axis = _args.getOpt<int>("axis", 3, 0, &_lock); + int bound = _args.getOpt<int>("bound", 4, 0, &_lock); + _retval = getPyNone(); + checkSymmetry(a, err, symmetrize, axis, bound); + _args.check(); + } + pbFinalizePlugin(parent, "checkSymmetry", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("checkSymmetry", e.what()); + return 0; + } +} +static const Pb::Register _RP_checkSymmetry("", "checkSymmetry", _W_10); +extern "C" { +void PbRegister_checkSymmetry() +{ + KEEP_UNUSED(_RP_checkSymmetry); +} +} + +//! check for symmetry , mac grid version + +void checkSymmetryVec3(Grid<Vec3> &a, + Grid<Real> *err = NULL, + bool symmetrize = false, + int axis = 0, + int bound = 0, + int disable = 0) +{ + if (err) + err->setConst(0.); + + // each dimension is measured separately for flexibility (could be combined) + const int c = axis; + const int o1 = (c + 1) % 3; + const int o2 = (c + 2) % 3; + + // x + if (!(disable & 1)) { + const int s = a.getSize()[c] + 1; + FOR_IJK(a) + { + Vec3i idx(i, j, k), mdx(i, j, k); + mdx[c] = s - 1 - idx[c]; + if (mdx[c] >= a.getSize()[c]) + continue; + if (bound > 0 && ((!a.isInBounds(idx, bound)) || (!a.isInBounds(mdx, bound)))) + continue; + + // special case: center "line" of values , should be zero! + if (mdx[c] == idx[c]) { + if (err) + (*err)(idx) += fabs((double)(a(idx)[c])); + if (symmetrize) + a(idx)[c] = 0.; + continue; + } + + // note - the a(mdx) component needs to be inverted here! + if (err) + (*err)(idx) += fabs((double)(a(idx)[c] - (a(mdx)[c] * -1.))); + if (symmetrize && (idx[c] < s / 2)) { + a(idx)[c] = a(mdx)[c] * -1.; + } + } + } + + // y + if (!(disable & 2)) { + const int s = a.getSize()[c]; + FOR_IJK(a) + { + Vec3i idx(i, j, k), mdx(i, j, k); + mdx[c] = s - 1 - idx[c]; + if (bound > 0 && ((!a.isInBounds(idx, bound)) || (!a.isInBounds(mdx, bound)))) + continue; + + if (err) + (*err)(idx) += fabs((double)(a(idx)[o1] - a(mdx)[o1])); + if (symmetrize && (idx[c] < s / 2)) { + a(idx)[o1] = a(mdx)[o1]; + } + } + } + + // z + if (!(disable & 4)) { + const int s = a.getSize()[c]; + FOR_IJK(a) + { + Vec3i idx(i, j, k), mdx(i, j, k); + mdx[c] = s - 1 - idx[c]; + if (bound > 0 && ((!a.isInBounds(idx, bound)) || (!a.isInBounds(mdx, bound)))) + continue; + + if (err) + (*err)(idx) += fabs((double)(a(idx)[o2] - a(mdx)[o2])); + if (symmetrize && (idx[c] < s / 2)) { + a(idx)[o2] = a(mdx)[o2]; + } + } + } +} +static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "checkSymmetryVec3", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Vec3> &a = *_args.getPtr<Grid<Vec3>>("a", 0, &_lock); + Grid<Real> *err = _args.getPtrOpt<Grid<Real>>("err", 1, NULL, &_lock); + bool symmetrize = _args.getOpt<bool>("symmetrize", 2, false, &_lock); + int axis = _args.getOpt<int>("axis", 3, 0, &_lock); + int bound = _args.getOpt<int>("bound", 4, 0, &_lock); + int disable = _args.getOpt<int>("disable", 5, 0, &_lock); + _retval = getPyNone(); + checkSymmetryVec3(a, err, symmetrize, axis, bound, disable); + _args.check(); + } + pbFinalizePlugin(parent, "checkSymmetryVec3", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("checkSymmetryVec3", e.what()); + return 0; + } +} +static const Pb::Register _RP_checkSymmetryVec3("", "checkSymmetryVec3", _W_11); +extern "C" { +void PbRegister_checkSymmetryVec3() +{ + KEEP_UNUSED(_RP_checkSymmetryVec3); +} +} + +// from simpleimage.cpp +void projectImg(SimpleImage &img, const Grid<Real> &val, int shadeMode = 0, Real scale = 1.); + +//! output shaded (all 3 axes at once for 3D) +//! shading modes: 0 smoke, 1 surfaces + +void projectPpmFull(const Grid<Real> &val, string name, int shadeMode = 0, Real scale = 1.) +{ + SimpleImage img; + projectImg(img, val, shadeMode, scale); + img.writePpm(name); +} +static PyObject *_W_12(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "projectPpmFull", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<Real> &val = *_args.getPtr<Grid<Real>>("val", 0, &_lock); + string name = _args.get<string>("name", 1, &_lock); + int shadeMode = _args.getOpt<int>("shadeMode", 2, 0, &_lock); + Real scale = _args.getOpt<Real>("scale", 3, 1., &_lock); + _retval = getPyNone(); + projectPpmFull(val, name, shadeMode, scale); + _args.check(); + } + pbFinalizePlugin(parent, "projectPpmFull", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("projectPpmFull", e.what()); + return 0; + } +} +static const Pb::Register _RP_projectPpmFull("", "projectPpmFull", _W_12); +extern "C" { +void PbRegister_projectPpmFull() +{ + KEEP_UNUSED(_RP_projectPpmFull); +} +} + +// helper functions for pdata operator tests + +//! init some test particles at the origin + +void addTestParts(BasicParticleSystem &parts, int num) +{ + for (int i = 0; i < num; ++i) + parts.addBuffered(Vec3(0, 0, 0)); + + parts.doCompress(); + parts.insertBufferedParticles(); +} +static PyObject *_W_13(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "addTestParts", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + int num = _args.get<int>("num", 1, &_lock); + _retval = getPyNone(); + addTestParts(parts, num); + _args.check(); + } + pbFinalizePlugin(parent, "addTestParts", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("addTestParts", e.what()); + return 0; + } +} +static const Pb::Register _RP_addTestParts("", "addTestParts", _W_13); +extern "C" { +void PbRegister_addTestParts() +{ + KEEP_UNUSED(_RP_addTestParts); +} +} + +//! calculate the difference between two pdata fields (note - slow!, not parallelized) + +Real pdataMaxDiff(const ParticleDataBase *a, const ParticleDataBase *b) +{ + double maxVal = 0.; + // debMsg(" PD "<< a->getType()<<" as"<<a->getSizeSlow()<<" bs"<<b->getSizeSlow() , 1); + assertMsg(a->getType() == b->getType(), "pdataMaxDiff problem - different pdata types!"); + assertMsg(a->getSizeSlow() == b->getSizeSlow(), "pdataMaxDiff problem - different pdata sizes!"); + + if (a->getType() & ParticleDataBase::TypeReal) { + const ParticleDataImpl<Real> &av = *dynamic_cast<const ParticleDataImpl<Real> *>(a); + const ParticleDataImpl<Real> &bv = *dynamic_cast<const ParticleDataImpl<Real> *>(b); + FOR_PARTS(av) + { + maxVal = std::max(maxVal, (double)fabs(av[idx] - bv[idx])); + } + } + else if (a->getType() & ParticleDataBase::TypeInt) { + const ParticleDataImpl<int> &av = *dynamic_cast<const ParticleDataImpl<int> *>(a); + const ParticleDataImpl<int> &bv = *dynamic_cast<const ParticleDataImpl<int> *>(b); + FOR_PARTS(av) + { + maxVal = std::max(maxVal, (double)fabs((double)av[idx] - bv[idx])); + } + } + else if (a->getType() & ParticleDataBase::TypeVec3) { + const ParticleDataImpl<Vec3> &av = *dynamic_cast<const ParticleDataImpl<Vec3> *>(a); + const ParticleDataImpl<Vec3> &bv = *dynamic_cast<const ParticleDataImpl<Vec3> *>(b); + FOR_PARTS(av) + { + double d = 0.; + for (int c = 0; c < 3; ++c) { + d += fabs((double)av[idx][c] - (double)bv[idx][c]); + } + maxVal = std::max(maxVal, d); + } + } + else { + errMsg("pdataMaxDiff: Grid Type is not supported (only Real, Vec3, int)"); + } + + return maxVal; +} +static PyObject *_W_14(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "pdataMaxDiff", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const ParticleDataBase *a = _args.getPtr<ParticleDataBase>("a", 0, &_lock); + const ParticleDataBase *b = _args.getPtr<ParticleDataBase>("b", 1, &_lock); + _retval = toPy(pdataMaxDiff(a, b)); + _args.check(); + } + pbFinalizePlugin(parent, "pdataMaxDiff", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("pdataMaxDiff", e.what()); + return 0; + } +} +static const Pb::Register _RP_pdataMaxDiff("", "pdataMaxDiff", _W_14); +extern "C" { +void PbRegister_pdataMaxDiff() +{ + KEEP_UNUSED(_RP_pdataMaxDiff); +} +} + +//! calculate center of mass given density grid, for re-centering + +Vec3 calcCenterOfMass(const Grid<Real> &density) +{ + Vec3 p(0.0f); + Real w = 0.0f; + FOR_IJK(density) + { + p += density(i, j, k) * Vec3(i + 0.5f, j + 0.5f, k + 0.5f); + w += density(i, j, k); + } + if (w > 1e-6f) + p /= w; + return p; +} +static PyObject *_W_15(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "calcCenterOfMass", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 0, &_lock); + _retval = toPy(calcCenterOfMass(density)); + _args.check(); + } + pbFinalizePlugin(parent, "calcCenterOfMass", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("calcCenterOfMass", e.what()); + return 0; + } +} +static const Pb::Register _RP_calcCenterOfMass("", "calcCenterOfMass", _W_15); +extern "C" { +void PbRegister_calcCenterOfMass() +{ + KEEP_UNUSED(_RP_calcCenterOfMass); +} +} + +//***************************************************************************** +// helper functions for volume fractions (which are needed for second order obstacle boundaries) + +inline static Real calcFraction(Real phi1, Real phi2, Real fracThreshold) +{ + if (phi1 > 0. && phi2 > 0.) + return 1.; + if (phi1 < 0. && phi2 < 0.) + return 0.; + + // make sure phi1 < phi2 + if (phi2 < phi1) { + Real t = phi1; + phi1 = phi2; + phi2 = t; + } + Real denom = phi1 - phi2; + if (denom > -1e-04) + return 0.5; + + Real frac = 1. - phi1 / denom; + if (frac < fracThreshold) + frac = 0.; // stomp small values , dont mark as fluid + return std::min(Real(1), frac); +} + +struct KnUpdateFractions : public KernelBase { + KnUpdateFractions(const FlagGrid &flags, + const Grid<Real> &phiObs, + MACGrid &fractions, + const int &boundaryWidth, + const Real fracThreshold) + : KernelBase(&flags, 1), + flags(flags), + phiObs(phiObs), + fractions(fractions), + boundaryWidth(boundaryWidth), + fracThreshold(fracThreshold) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const Grid<Real> &phiObs, + MACGrid &fractions, + const int &boundaryWidth, + const Real fracThreshold) const + { + + // walls at domain bounds and inner objects + fractions(i, j, k).x = calcFraction(phiObs(i, j, k), phiObs(i - 1, j, k), fracThreshold); + fractions(i, j, k).y = calcFraction(phiObs(i, j, k), phiObs(i, j - 1, k), fracThreshold); + if (phiObs.is3D()) { + fractions(i, j, k).z = calcFraction(phiObs(i, j, k), phiObs(i, j, k - 1), fracThreshold); + } + + // remaining BCs at the domain boundaries + const int w = boundaryWidth; + // only set if not in obstacle + if (phiObs(i, j, k) < 0.) + return; + + // x-direction boundaries + if (i <= w + 1) { // min x + if ((flags.isInflow(i - 1, j, k)) || (flags.isOutflow(i - 1, j, k)) || + (flags.isOpen(i - 1, j, k))) { + fractions(i, j, k).x = fractions(i, j, k).y = 1.; + if (flags.is3D()) + fractions(i, j, k).z = 1.; + } + } + if (i >= flags.getSizeX() - w - 2) { // max x + if ((flags.isInflow(i + 1, j, k)) || (flags.isOutflow(i + 1, j, k)) || + (flags.isOpen(i + 1, j, k))) { + fractions(i + 1, j, k).x = fractions(i + 1, j, k).y = 1.; + if (flags.is3D()) + fractions(i + 1, j, k).z = 1.; + } + } + // y-direction boundaries + if (j <= w + 1) { // min y + if ((flags.isInflow(i, j - 1, k)) || (flags.isOutflow(i, j - 1, k)) || + (flags.isOpen(i, j - 1, k))) { + fractions(i, j, k).x = fractions(i, j, k).y = 1.; + if (flags.is3D()) + fractions(i, j, k).z = 1.; + } + } + if (j >= flags.getSizeY() - w - 2) { // max y + if ((flags.isInflow(i, j + 1, k)) || (flags.isOutflow(i, j + 1, k)) || + (flags.isOpen(i, j + 1, k))) { + fractions(i, j + 1, k).x = fractions(i, j + 1, k).y = 1.; + if (flags.is3D()) + fractions(i, j + 1, k).z = 1.; + } + } + // z-direction boundaries + if (flags.is3D()) { + if (k <= w + 1) { // min z + if ((flags.isInflow(i, j, k - 1)) || (flags.isOutflow(i, j, k - 1)) || + (flags.isOpen(i, j, k - 1))) { + fractions(i, j, k).x = fractions(i, j, k).y = 1.; + if (flags.is3D()) + fractions(i, j, k).z = 1.; + } + } + if (j >= flags.getSizeZ() - w - 2) { // max z + if ((flags.isInflow(i, j, k + 1)) || (flags.isOutflow(i, j, k + 1)) || + (flags.isOpen(i, j, k + 1))) { + fractions(i, j, k + 1).x = fractions(i, j, k + 1).y = 1.; + if (flags.is3D()) + fractions(i, j, k + 1).z = 1.; + } + } + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const Grid<Real> &getArg1() + { + return phiObs; + } + typedef Grid<Real> type1; + inline MACGrid &getArg2() + { + return fractions; + } + typedef MACGrid type2; + inline const int &getArg3() + { + return boundaryWidth; + } + typedef int type3; + inline const Real &getArg4() + { + return fracThreshold; + } + typedef Real type4; + void runMessage() + { + debMsg("Executing kernel KnUpdateFractions ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, phiObs, fractions, boundaryWidth, fracThreshold); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, phiObs, fractions, boundaryWidth, fracThreshold); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + const Grid<Real> &phiObs; + MACGrid &fractions; + const int &boundaryWidth; + const Real fracThreshold; +}; + +//! update fill fraction values +void updateFractions(const FlagGrid &flags, + const Grid<Real> &phiObs, + MACGrid &fractions, + const int &boundaryWidth = 0, + const Real fracThreshold = 0.01) +{ + fractions.setConst(Vec3(0.)); + KnUpdateFractions(flags, phiObs, fractions, boundaryWidth, fracThreshold); +} +static PyObject *_W_16(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "updateFractions", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + const Grid<Real> &phiObs = *_args.getPtr<Grid<Real>>("phiObs", 1, &_lock); + MACGrid &fractions = *_args.getPtr<MACGrid>("fractions", 2, &_lock); + const int &boundaryWidth = _args.getOpt<int>("boundaryWidth", 3, 0, &_lock); + const Real fracThreshold = _args.getOpt<Real>("fracThreshold", 4, 0.01, &_lock); + _retval = getPyNone(); + updateFractions(flags, phiObs, fractions, boundaryWidth, fracThreshold); + _args.check(); + } + pbFinalizePlugin(parent, "updateFractions", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("updateFractions", e.what()); + return 0; + } +} +static const Pb::Register _RP_updateFractions("", "updateFractions", _W_16); +extern "C" { +void PbRegister_updateFractions() +{ + KEEP_UNUSED(_RP_updateFractions); +} +} + +struct KnUpdateFlagsObs : public KernelBase { + KnUpdateFlagsObs(FlagGrid &flags, + const MACGrid *fractions, + const Grid<Real> &phiObs, + const Grid<Real> *phiOut, + const Grid<Real> *phiIn) + : KernelBase(&flags, 1), + flags(flags), + fractions(fractions), + phiObs(phiObs), + phiOut(phiOut), + phiIn(phiIn) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + FlagGrid &flags, + const MACGrid *fractions, + const Grid<Real> &phiObs, + const Grid<Real> *phiOut, + const Grid<Real> *phiIn) const + { + + bool isObs = false; + if (fractions) { + Real f = 0.; + f += fractions->get(i, j, k).x; + f += fractions->get(i + 1, j, k).x; + f += fractions->get(i, j, k).y; + f += fractions->get(i, j + 1, k).y; + if (flags.is3D()) { + f += fractions->get(i, j, k).z; + f += fractions->get(i, j, k + 1).z; + } + if (f == 0.) + isObs = true; + } + else { + if (phiObs(i, j, k) < 0.) + isObs = true; + } + + bool isOutflow = false; + bool isInflow = false; + if (phiOut && (*phiOut)(i, j, k) < 0.) + isOutflow = true; + if (phiIn && (*phiIn)(i, j, k) < 0.) + isInflow = true; + + if (isObs) + flags(i, j, k) = FlagGrid::TypeObstacle; + else if (isInflow) + flags(i, j, k) = (FlagGrid::TypeFluid | FlagGrid::TypeInflow); + else if (isOutflow) + flags(i, j, k) = (FlagGrid::TypeEmpty | FlagGrid::TypeOutflow); + else + flags(i, j, k) = FlagGrid::TypeEmpty; + } + inline FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid *getArg1() + { + return fractions; + } + typedef MACGrid type1; + inline const Grid<Real> &getArg2() + { + return phiObs; + } + typedef Grid<Real> type2; + inline const Grid<Real> *getArg3() + { + return phiOut; + } + typedef Grid<Real> type3; + inline const Grid<Real> *getArg4() + { + return phiIn; + } + typedef Grid<Real> type4; + void runMessage() + { + debMsg("Executing kernel KnUpdateFlagsObs ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, fractions, phiObs, phiOut, phiIn); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, fractions, phiObs, phiOut, phiIn); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + FlagGrid &flags; + const MACGrid *fractions; + const Grid<Real> &phiObs; + const Grid<Real> *phiOut; + const Grid<Real> *phiIn; +}; + +//! update obstacle and outflow flags from levelsets +//! optionally uses fill fractions for obstacle +void setObstacleFlags(FlagGrid &flags, + const Grid<Real> &phiObs, + const MACGrid *fractions = NULL, + const Grid<Real> *phiOut = NULL, + const Grid<Real> *phiIn = NULL) +{ + KnUpdateFlagsObs(flags, fractions, phiObs, phiOut, phiIn); +} +static PyObject *_W_17(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setObstacleFlags", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + const Grid<Real> &phiObs = *_args.getPtr<Grid<Real>>("phiObs", 1, &_lock); + const MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 2, NULL, &_lock); + const Grid<Real> *phiOut = _args.getPtrOpt<Grid<Real>>("phiOut", 3, NULL, &_lock); + const Grid<Real> *phiIn = _args.getPtrOpt<Grid<Real>>("phiIn", 4, NULL, &_lock); + _retval = getPyNone(); + setObstacleFlags(flags, phiObs, fractions, phiOut, phiIn); + _args.check(); + } + pbFinalizePlugin(parent, "setObstacleFlags", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setObstacleFlags", e.what()); + return 0; + } +} +static const Pb::Register _RP_setObstacleFlags("", "setObstacleFlags", _W_17); +extern "C" { +void PbRegister_setObstacleFlags() +{ + KEEP_UNUSED(_RP_setObstacleFlags); +} +} + +//! small helper for test case test_1040_secOrderBnd.py +struct kninitVortexVelocity : public KernelBase { + kninitVortexVelocity(const Grid<Real> &phiObs, + MACGrid &vel, + const Vec3 ¢er, + const Real &radius) + : KernelBase(&phiObs, 0), phiObs(phiObs), vel(vel), center(center), radius(radius) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const Grid<Real> &phiObs, + MACGrid &vel, + const Vec3 ¢er, + const Real &radius) const + { + + if (phiObs(i, j, k) >= -1.) { + + Real dx = i - center.x; + if (dx >= 0) + dx -= .5; + else + dx += .5; + Real dy = j - center.y; + Real r = std::sqrt(dx * dx + dy * dy); + Real alpha = atan2(dy, dx); + + vel(i, j, k).x = -std::sin(alpha) * (r / radius); + + dx = i - center.x; + dy = j - center.y; + if (dy >= 0) + dy -= .5; + else + dy += .5; + r = std::sqrt(dx * dx + dy * dy); + alpha = atan2(dy, dx); + + vel(i, j, k).y = std::cos(alpha) * (r / radius); + } + } + inline const Grid<Real> &getArg0() + { + return phiObs; + } + typedef Grid<Real> type0; + inline MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline const Vec3 &getArg2() + { + return center; + } + typedef Vec3 type2; + inline const Real &getArg3() + { + return radius; + } + typedef Real type3; + void runMessage() + { + debMsg("Executing kernel kninitVortexVelocity ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, phiObs, vel, center, radius); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, phiObs, vel, center, radius); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const Grid<Real> &phiObs; + MACGrid &vel; + const Vec3 ¢er; + const Real &radius; +}; + +void initVortexVelocity(const Grid<Real> &phiObs, + MACGrid &vel, + const Vec3 ¢er, + const Real &radius) +{ + kninitVortexVelocity(phiObs, vel, center, radius); +} +static PyObject *_W_18(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "initVortexVelocity", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<Real> &phiObs = *_args.getPtr<Grid<Real>>("phiObs", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const Vec3 ¢er = _args.get<Vec3>("center", 2, &_lock); + const Real &radius = _args.get<Real>("radius", 3, &_lock); + _retval = getPyNone(); + initVortexVelocity(phiObs, vel, center, radius); + _args.check(); + } + pbFinalizePlugin(parent, "initVortexVelocity", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("initVortexVelocity", e.what()); + return 0; + } +} +static const Pb::Register _RP_initVortexVelocity("", "initVortexVelocity", _W_18); +extern "C" { +void PbRegister_initVortexVelocity() +{ + KEEP_UNUSED(_RP_initVortexVelocity); +} +} + +//***************************************************************************** +// helper functions for blurring + +//! class for Gaussian Blur +struct GaussianKernelCreator { + public: + float mSigma; + int mDim; + float *mMat1D; + + GaussianKernelCreator() : mSigma(0.0f), mDim(0), mMat1D(NULL) + { + } + GaussianKernelCreator(float sigma, int dim = 0) : mSigma(0.0f), mDim(0), mMat1D(NULL) + { + setGaussianSigma(sigma, dim); + } + + Real getWeiAtDis(float disx, float disy) + { + float m = 1.0 / (sqrt(2.0 * M_PI) * mSigma); + float v = m * exp(-(1.0 * disx * disx + 1.0 * disy * disy) / (2.0 * mSigma * mSigma)); + return v; + } + + Real getWeiAtDis(float disx, float disy, float disz) + { + float m = 1.0 / (sqrt(2.0 * M_PI) * mSigma); + float v = m * exp(-(1.0 * disx * disx + 1.0 * disy * disy + 1.0 * disz * disz) / + (2.0 * mSigma * mSigma)); + return v; + } + + void setGaussianSigma(float sigma, int dim = 0) + { + mSigma = sigma; + if (dim < 3) + mDim = (int)(2.0 * 3.0 * sigma + 1.0f); + else + mDim = dim; + if (mDim < 3) + mDim = 3; + + if (mDim % 2 == 0) + ++mDim; // make dim odd + + float s2 = mSigma * mSigma; + int c = mDim / 2; + float m = 1.0 / (sqrt(2.0 * M_PI) * mSigma); + + // create 1D matrix + if (mMat1D) + delete[] mMat1D; + mMat1D = new float[mDim]; + for (int i = 0; i < (mDim + 1) / 2; i++) { + float v = m * exp(-(1.0 * i * i) / (2.0 * s2)); + mMat1D[c + i] = v; + mMat1D[c - i] = v; + } + } + + ~GaussianKernelCreator() + { + if (mMat1D) + delete[] mMat1D; + } + + float get1DKernelValue(int off) + { + assertMsg(off >= 0 && off < mDim, "off exceeded boundary in Gaussian Kernel 1D!"); + return mMat1D[off]; + } +}; + +template<class T> +T convolveGrid(Grid<T> &originGrid, GaussianKernelCreator &gkSigma, Vec3 pos, int cdir) +{ + // pos should be the centre pos, e.g., 1.5, 4.5, 0.5 for grid pos 1,4,0 + Vec3 step(1.0, 0.0, 0.0); + if (cdir == 1) // todo, z + step = Vec3(0.0, 1.0, 0.0); + else if (cdir == 2) + step = Vec3(0.0, 0.0, 1.0); + T pxResult(0); + for (int i = 0; i < gkSigma.mDim; ++i) { + Vec3i curpos = toVec3i(pos - step * (i - gkSigma.mDim / 2)); + if (originGrid.isInBounds(curpos)) + pxResult += gkSigma.get1DKernelValue(i) * originGrid.get(curpos); + else { // TODO , improve... + Vec3i curfitpos = curpos; + if (curfitpos.x < 0) + curfitpos.x = 0; + else if (curfitpos.x >= originGrid.getSizeX()) + curfitpos.x = originGrid.getSizeX() - 1; + if (curfitpos.y < 0) + curfitpos.y = 0; + else if (curfitpos.y >= originGrid.getSizeY()) + curfitpos.y = originGrid.getSizeY() - 1; + if (curfitpos.z < 0) + curfitpos.z = 0; + else if (curfitpos.z >= originGrid.getSizeZ()) + curfitpos.z = originGrid.getSizeZ() - 1; + pxResult += gkSigma.get1DKernelValue(i) * originGrid.get(curfitpos); + } + } + return pxResult; +} + +template<class T> struct knBlurGrid : public KernelBase { + knBlurGrid(Grid<T> &originGrid, Grid<T> &targetGrid, GaussianKernelCreator &gkSigma, int cdir) + : KernelBase(&originGrid, 0), + originGrid(originGrid), + targetGrid(targetGrid), + gkSigma(gkSigma), + cdir(cdir) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<T> &originGrid, + Grid<T> &targetGrid, + GaussianKernelCreator &gkSigma, + int cdir) const + { + targetGrid(i, j, k) = convolveGrid<T>(originGrid, gkSigma, Vec3(i, j, k), cdir); + } + inline Grid<T> &getArg0() + { + return originGrid; + } + typedef Grid<T> type0; + inline Grid<T> &getArg1() + { + return targetGrid; + } + typedef Grid<T> type1; + inline GaussianKernelCreator &getArg2() + { + return gkSigma; + } + typedef GaussianKernelCreator type2; + inline int &getArg3() + { + return cdir; + } + typedef int type3; + void runMessage() + { + debMsg("Executing kernel knBlurGrid ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, originGrid, targetGrid, gkSigma, cdir); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, originGrid, targetGrid, gkSigma, cdir); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + Grid<T> &originGrid; + Grid<T> &targetGrid; + GaussianKernelCreator &gkSigma; + int cdir; +}; + +template<class T> int blurGrid(Grid<T> &originGrid, Grid<T> &targetGrid, float sigma) +{ + GaussianKernelCreator tmGK(sigma); + Grid<T> tmpGrid(originGrid); + knBlurGrid<T>(originGrid, tmpGrid, tmGK, 0); // blur x + knBlurGrid<T>(tmpGrid, targetGrid, tmGK, 1); // blur y + if (targetGrid.is3D()) { + tmpGrid.copyFrom(targetGrid); + knBlurGrid<T>(tmpGrid, targetGrid, tmGK, 2); + } + return tmGK.mDim; +} + +struct KnBlurMACGridGauss : public KernelBase { + KnBlurMACGridGauss(MACGrid &originGrid, + MACGrid &target, + GaussianKernelCreator &gkSigma, + int cdir) + : KernelBase(&originGrid, 0), + originGrid(originGrid), + target(target), + gkSigma(gkSigma), + cdir(cdir) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + MACGrid &originGrid, + MACGrid &target, + GaussianKernelCreator &gkSigma, + int cdir) const + { + Vec3 pos(i, j, k); + Vec3 step(1.0, 0.0, 0.0); + if (cdir == 1) + step = Vec3(0.0, 1.0, 0.0); + else if (cdir == 2) + step = Vec3(0.0, 0.0, 1.0); + + Vec3 pxResult(0.0f); + for (int di = 0; di < gkSigma.mDim; ++di) { + Vec3i curpos = toVec3i(pos - step * (di - gkSigma.mDim / 2)); + if (!originGrid.isInBounds(curpos)) { + if (curpos.x < 0) + curpos.x = 0; + else if (curpos.x >= originGrid.getSizeX()) + curpos.x = originGrid.getSizeX() - 1; + if (curpos.y < 0) + curpos.y = 0; + else if (curpos.y >= originGrid.getSizeY()) + curpos.y = originGrid.getSizeY() - 1; + if (curpos.z < 0) + curpos.z = 0; + else if (curpos.z >= originGrid.getSizeZ()) + curpos.z = originGrid.getSizeZ() - 1; + } + pxResult += gkSigma.get1DKernelValue(di) * originGrid.get(curpos); + } + target(i, j, k) = pxResult; + } + inline MACGrid &getArg0() + { + return originGrid; + } + typedef MACGrid type0; + inline MACGrid &getArg1() + { + return target; + } + typedef MACGrid type1; + inline GaussianKernelCreator &getArg2() + { + return gkSigma; + } + typedef GaussianKernelCreator type2; + inline int &getArg3() + { + return cdir; + } + typedef int type3; + void runMessage() + { + debMsg("Executing kernel KnBlurMACGridGauss ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, originGrid, target, gkSigma, cdir); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, originGrid, target, gkSigma, cdir); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + MACGrid &originGrid; + MACGrid ⌖ + GaussianKernelCreator &gkSigma; + int cdir; +}; + +int blurMacGrid(MACGrid &oG, MACGrid &tG, float si) +{ + GaussianKernelCreator tmGK(si); + MACGrid tmpGrid(oG); + KnBlurMACGridGauss(oG, tmpGrid, tmGK, 0); // blur x + KnBlurMACGridGauss(tmpGrid, tG, tmGK, 1); // blur y + if (tG.is3D()) { + tmpGrid.copyFrom(tG); + KnBlurMACGridGauss(tmpGrid, tG, tmGK, 2); + } + return tmGK.mDim; +} +static PyObject *_W_19(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "blurMacGrid", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &oG = *_args.getPtr<MACGrid>("oG", 0, &_lock); + MACGrid &tG = *_args.getPtr<MACGrid>("tG", 1, &_lock); + float si = _args.get<float>("si", 2, &_lock); + _retval = toPy(blurMacGrid(oG, tG, si)); + _args.check(); + } + pbFinalizePlugin(parent, "blurMacGrid", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("blurMacGrid", e.what()); + return 0; + } +} +static const Pb::Register _RP_blurMacGrid("", "blurMacGrid", _W_19); +extern "C" { +void PbRegister_blurMacGrid() +{ + KEEP_UNUSED(_RP_blurMacGrid); +} +} + +int blurRealGrid(Grid<Real> &oG, Grid<Real> &tG, float si) +{ + return blurGrid<Real>(oG, tG, si); +} +static PyObject *_W_20(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "blurRealGrid", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &oG = *_args.getPtr<Grid<Real>>("oG", 0, &_lock); + Grid<Real> &tG = *_args.getPtr<Grid<Real>>("tG", 1, &_lock); + float si = _args.get<float>("si", 2, &_lock); + _retval = toPy(blurRealGrid(oG, tG, si)); + _args.check(); + } + pbFinalizePlugin(parent, "blurRealGrid", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("blurRealGrid", e.what()); + return 0; + } +} +static const Pb::Register _RP_blurRealGrid("", "blurRealGrid", _W_20); +extern "C" { +void PbRegister_blurRealGrid() +{ + KEEP_UNUSED(_RP_blurRealGrid); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/kepsilon.cpp b/extern/mantaflow/preprocessed/plugin/kepsilon.cpp new file mode 100644 index 00000000000..306db9e20cc --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/kepsilon.cpp @@ -0,0 +1,578 @@ + + +// 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 + * + * Turbulence modeling plugins + * + ******************************************************************************/ + +#include "grid.h" +#include "commonkernels.h" +#include "vortexsheet.h" +#include "conjugategrad.h" + +using namespace std; + +namespace Manta { + +// k-epsilon model constants +const Real keCmu = 0.09; +const Real keC1 = 1.44; +const Real keC2 = 1.92; +const Real keS1 = 1.0; +const Real keS2 = 1.3; + +// k-epsilon limiters +const Real keU0 = 1.0; +const Real keImin = 2e-3; +const Real keImax = 1.0; +const Real keNuMin = 1e-3; +const Real keNuMax = 5.0; + +//! clamp k and epsilon to limits + +struct KnTurbulenceClamp : public KernelBase { + KnTurbulenceClamp( + Grid<Real> &kgrid, Grid<Real> &egrid, Real minK, Real maxK, Real minNu, Real maxNu) + : KernelBase(&kgrid, 0), + kgrid(kgrid), + egrid(egrid), + minK(minK), + maxK(maxK), + minNu(minNu), + maxNu(maxNu) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + Grid<Real> &kgrid, + Grid<Real> &egrid, + Real minK, + Real maxK, + Real minNu, + Real maxNu) const + { + Real eps = egrid[idx]; + Real ke = clamp(kgrid[idx], minK, maxK); + Real nu = keCmu * square(ke) / eps; + if (nu > maxNu) + eps = keCmu * square(ke) / maxNu; + if (nu < minNu) + eps = keCmu * square(ke) / minNu; + + kgrid[idx] = ke; + egrid[idx] = eps; + } + inline Grid<Real> &getArg0() + { + return kgrid; + } + typedef Grid<Real> type0; + inline Grid<Real> &getArg1() + { + return egrid; + } + typedef Grid<Real> type1; + inline Real &getArg2() + { + return minK; + } + typedef Real type2; + inline Real &getArg3() + { + return maxK; + } + typedef Real type3; + inline Real &getArg4() + { + return minNu; + } + typedef Real type4; + inline Real &getArg5() + { + return maxNu; + } + typedef Real type5; + void runMessage() + { + debMsg("Executing kernel KnTurbulenceClamp ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, kgrid, egrid, minK, maxK, minNu, maxNu); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + Grid<Real> &kgrid; + Grid<Real> &egrid; + Real minK; + Real maxK; + Real minNu; + Real maxNu; +}; + +//! Compute k-epsilon production term P = 2*nu_T*sum_ij(Sij^2) and the turbulent viscosity +//! nu_T=C_mu*k^2/eps + +struct KnComputeProduction : public KernelBase { + KnComputeProduction(const MACGrid &vel, + const Grid<Vec3> &velCenter, + const Grid<Real> &ke, + const Grid<Real> &eps, + Grid<Real> &prod, + Grid<Real> &nuT, + Grid<Real> *strain, + Real pscale = 1.0f) + : KernelBase(&vel, 1), + vel(vel), + velCenter(velCenter), + ke(ke), + eps(eps), + prod(prod), + nuT(nuT), + strain(strain), + pscale(pscale) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const MACGrid &vel, + const Grid<Vec3> &velCenter, + const Grid<Real> &ke, + const Grid<Real> &eps, + Grid<Real> &prod, + Grid<Real> &nuT, + Grid<Real> *strain, + Real pscale = 1.0f) const + { + Real curEps = eps(i, j, k); + if (curEps > 0) { + // turbulent viscosity: nu_T = C_mu * k^2/eps + Real curNu = keCmu * square(ke(i, j, k)) / curEps; + + // compute Sij = 1/2 * (dU_i/dx_j + dU_j/dx_i) + Vec3 diag = Vec3(vel(i + 1, j, k).x, vel(i, j + 1, k).y, vel(i, j, k + 1).z) - vel(i, j, k); + Vec3 ux = 0.5 * (velCenter(i + 1, j, k) - velCenter(i - 1, j, k)); + Vec3 uy = 0.5 * (velCenter(i, j + 1, k) - velCenter(i, j - 1, k)); + Vec3 uz = 0.5 * (velCenter(i, j, k + 1) - velCenter(i, j, k - 1)); + Real S12 = 0.5 * (ux.y + uy.x); + Real S13 = 0.5 * (ux.z + uz.x); + Real S23 = 0.5 * (uy.z + uz.y); + Real S2 = square(diag.x) + square(diag.y) + square(diag.z) + 2.0 * square(S12) + + 2.0 * square(S13) + 2.0 * square(S23); + + // P = 2*nu_T*sum_ij(Sij^2) + prod(i, j, k) = 2.0 * curNu * S2 * pscale; + nuT(i, j, k) = curNu; + if (strain) + (*strain)(i, j, k) = sqrt(S2); + } + else { + prod(i, j, k) = 0; + nuT(i, j, k) = 0; + if (strain) + (*strain)(i, j, k) = 0; + } + } + inline const MACGrid &getArg0() + { + return vel; + } + typedef MACGrid type0; + inline const Grid<Vec3> &getArg1() + { + return velCenter; + } + typedef Grid<Vec3> type1; + inline const Grid<Real> &getArg2() + { + return ke; + } + typedef Grid<Real> type2; + inline const Grid<Real> &getArg3() + { + return eps; + } + typedef Grid<Real> type3; + inline Grid<Real> &getArg4() + { + return prod; + } + typedef Grid<Real> type4; + inline Grid<Real> &getArg5() + { + return nuT; + } + typedef Grid<Real> type5; + inline Grid<Real> *getArg6() + { + return strain; + } + typedef Grid<Real> type6; + inline Real &getArg7() + { + return pscale; + } + typedef Real type7; + void runMessage() + { + debMsg("Executing kernel KnComputeProduction ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, velCenter, ke, eps, prod, nuT, strain, pscale); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, velCenter, ke, eps, prod, nuT, strain, pscale); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const MACGrid &vel; + const Grid<Vec3> &velCenter; + const Grid<Real> &ke; + const Grid<Real> &eps; + Grid<Real> ∏ + Grid<Real> &nuT; + Grid<Real> *strain; + Real pscale; +}; + +//! Compute k-epsilon production term P = 2*nu_T*sum_ij(Sij^2) and the turbulent viscosity +//! nu_T=C_mu*k^2/eps + +void KEpsilonComputeProduction(const MACGrid &vel, + Grid<Real> &k, + Grid<Real> &eps, + Grid<Real> &prod, + Grid<Real> &nuT, + Grid<Real> *strain = 0, + Real pscale = 1.0f) +{ + // get centered velocity grid + Grid<Vec3> vcenter(k.getParent()); + GetCentered(vcenter, vel); + FillInBoundary(vcenter, 1); + + // compute limits + const Real minK = 1.5 * square(keU0) * square(keImin); + const Real maxK = 1.5 * square(keU0) * square(keImax); + KnTurbulenceClamp(k, eps, minK, maxK, keNuMin, keNuMax); + + KnComputeProduction(vel, vcenter, k, eps, prod, nuT, strain, pscale); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "KEpsilonComputeProduction", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + Grid<Real> &k = *_args.getPtr<Grid<Real>>("k", 1, &_lock); + Grid<Real> &eps = *_args.getPtr<Grid<Real>>("eps", 2, &_lock); + Grid<Real> &prod = *_args.getPtr<Grid<Real>>("prod", 3, &_lock); + Grid<Real> &nuT = *_args.getPtr<Grid<Real>>("nuT", 4, &_lock); + Grid<Real> *strain = _args.getPtrOpt<Grid<Real>>("strain", 5, 0, &_lock); + Real pscale = _args.getOpt<Real>("pscale", 6, 1.0f, &_lock); + _retval = getPyNone(); + KEpsilonComputeProduction(vel, k, eps, prod, nuT, strain, pscale); + _args.check(); + } + pbFinalizePlugin(parent, "KEpsilonComputeProduction", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("KEpsilonComputeProduction", e.what()); + return 0; + } +} +static const Pb::Register _RP_KEpsilonComputeProduction("", "KEpsilonComputeProduction", _W_0); +extern "C" { +void PbRegister_KEpsilonComputeProduction() +{ + KEEP_UNUSED(_RP_KEpsilonComputeProduction); +} +} + +//! Integrate source terms of k-epsilon equation + +struct KnAddTurbulenceSource : public KernelBase { + KnAddTurbulenceSource(Grid<Real> &kgrid, Grid<Real> &egrid, const Grid<Real> &pgrid, Real dt) + : KernelBase(&kgrid, 0), kgrid(kgrid), egrid(egrid), pgrid(pgrid), dt(dt) + { + runMessage(); + run(); + } + inline void op( + IndexInt idx, Grid<Real> &kgrid, Grid<Real> &egrid, const Grid<Real> &pgrid, Real dt) const + { + Real eps = egrid[idx], prod = pgrid[idx], ke = kgrid[idx]; + if (ke <= 0) + ke = 1e-3; // pre-clamp to avoid nan + + Real newK = ke + dt * (prod - eps); + Real newEps = eps + dt * (prod * keC1 - eps * keC2) * (eps / ke); + if (newEps <= 0) + newEps = 1e-4; // pre-clamp to avoid nan + + kgrid[idx] = newK; + egrid[idx] = newEps; + } + inline Grid<Real> &getArg0() + { + return kgrid; + } + typedef Grid<Real> type0; + inline Grid<Real> &getArg1() + { + return egrid; + } + typedef Grid<Real> type1; + inline const Grid<Real> &getArg2() + { + return pgrid; + } + typedef Grid<Real> type2; + inline Real &getArg3() + { + return dt; + } + typedef Real type3; + void runMessage() + { + debMsg("Executing kernel KnAddTurbulenceSource ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, kgrid, egrid, pgrid, dt); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + Grid<Real> &kgrid; + Grid<Real> &egrid; + const Grid<Real> &pgrid; + Real dt; +}; + +//! Integrate source terms of k-epsilon equation +void KEpsilonSources(Grid<Real> &k, Grid<Real> &eps, Grid<Real> &prod) +{ + Real dt = k.getParent()->getDt(); + + KnAddTurbulenceSource(k, eps, prod, dt); + + // compute limits + const Real minK = 1.5 * square(keU0) * square(keImin); + const Real maxK = 1.5 * square(keU0) * square(keImax); + KnTurbulenceClamp(k, eps, minK, maxK, keNuMin, keNuMax); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "KEpsilonSources", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &k = *_args.getPtr<Grid<Real>>("k", 0, &_lock); + Grid<Real> &eps = *_args.getPtr<Grid<Real>>("eps", 1, &_lock); + Grid<Real> &prod = *_args.getPtr<Grid<Real>>("prod", 2, &_lock); + _retval = getPyNone(); + KEpsilonSources(k, eps, prod); + _args.check(); + } + pbFinalizePlugin(parent, "KEpsilonSources", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("KEpsilonSources", e.what()); + return 0; + } +} +static const Pb::Register _RP_KEpsilonSources("", "KEpsilonSources", _W_1); +extern "C" { +void PbRegister_KEpsilonSources() +{ + KEEP_UNUSED(_RP_KEpsilonSources); +} +} + +//! Initialize the domain or boundary conditions +void KEpsilonBcs( + const FlagGrid &flags, Grid<Real> &k, Grid<Real> &eps, Real intensity, Real nu, bool fillArea) +{ + // compute limits + const Real vk = 1.5 * square(keU0) * square(intensity); + const Real ve = keCmu * square(vk) / nu; + + FOR_IDX(k) + { + if (fillArea || flags.isObstacle(idx)) { + k[idx] = vk; + eps[idx] = ve; + } + } +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "KEpsilonBcs", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &k = *_args.getPtr<Grid<Real>>("k", 1, &_lock); + Grid<Real> &eps = *_args.getPtr<Grid<Real>>("eps", 2, &_lock); + Real intensity = _args.get<Real>("intensity", 3, &_lock); + Real nu = _args.get<Real>("nu", 4, &_lock); + bool fillArea = _args.get<bool>("fillArea", 5, &_lock); + _retval = getPyNone(); + KEpsilonBcs(flags, k, eps, intensity, nu, fillArea); + _args.check(); + } + pbFinalizePlugin(parent, "KEpsilonBcs", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("KEpsilonBcs", e.what()); + return 0; + } +} +static const Pb::Register _RP_KEpsilonBcs("", "KEpsilonBcs", _W_2); +extern "C" { +void PbRegister_KEpsilonBcs() +{ + KEEP_UNUSED(_RP_KEpsilonBcs); +} +} + +//! Gradient diffusion smoothing. Not unconditionally stable -- should probably do substepping etc. +void ApplyGradDiff( + const Grid<Real> &grid, Grid<Real> &res, const Grid<Real> &nu, Real dt, Real sigma) +{ + // should do this (but requires better boundary handling) + /*MACGrid grad(grid.getParent()); + GradientOpMAC(grad, grid); + grad *= nu; + DivergenceOpMAC(res, grad); + res *= dt/sigma; */ + + LaplaceOp(res, grid); + res *= nu; + res *= dt / sigma; +} + +//! Compute k-epsilon turbulent viscosity +void KEpsilonGradientDiffusion( + Grid<Real> &k, Grid<Real> &eps, Grid<Real> &nuT, Real sigmaU = 4.0, MACGrid *vel = 0) +{ + Real dt = k.getParent()->getDt(); + Grid<Real> res(k.getParent()); + + // gradient diffusion of k + ApplyGradDiff(k, res, nuT, dt, keS1); + k += res; + + // gradient diffusion of epsilon + ApplyGradDiff(eps, res, nuT, dt, keS2); + eps += res; + + // gradient diffusion of velocity + if (vel) { + Grid<Real> vc(k.getParent()); + for (int c = 0; c < 3; c++) { + GetComponent(*vel, vc, c); + ApplyGradDiff(vc, res, nuT, dt, sigmaU); + vc += res; + SetComponent(*vel, vc, c); + } + } +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "KEpsilonGradientDiffusion", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &k = *_args.getPtr<Grid<Real>>("k", 0, &_lock); + Grid<Real> &eps = *_args.getPtr<Grid<Real>>("eps", 1, &_lock); + Grid<Real> &nuT = *_args.getPtr<Grid<Real>>("nuT", 2, &_lock); + Real sigmaU = _args.getOpt<Real>("sigmaU", 3, 4.0, &_lock); + MACGrid *vel = _args.getPtrOpt<MACGrid>("vel", 4, 0, &_lock); + _retval = getPyNone(); + KEpsilonGradientDiffusion(k, eps, nuT, sigmaU, vel); + _args.check(); + } + pbFinalizePlugin(parent, "KEpsilonGradientDiffusion", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("KEpsilonGradientDiffusion", e.what()); + return 0; + } +} +static const Pb::Register _RP_KEpsilonGradientDiffusion("", "KEpsilonGradientDiffusion", _W_3); +extern "C" { +void PbRegister_KEpsilonGradientDiffusion() +{ + KEEP_UNUSED(_RP_KEpsilonGradientDiffusion); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/meshplugins.cpp b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp new file mode 100644 index 00000000000..415bca153d0 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp @@ -0,0 +1,780 @@ + + +// 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 + * + * Smoothing etc. for meshes + * + ******************************************************************************/ + +/******************************************************************************/ +// Copyright note: +// +// These functions (C) Chris Wojtan +// Long-term goal is to unify with his split&merge codebase +// +/******************************************************************************/ + +#include <queue> +#include <algorithm> +#include "mesh.h" +#include "kernel.h" +#include "edgecollapse.h" +#include <mesh.h> +#include <stack> + +using namespace std; + +namespace Manta { + +//! Mesh smoothing +/*! see Desbrun 99 "Implicit fairing of of irregular meshes using diffusion and curvature flow"*/ +void smoothMesh(Mesh &mesh, Real strength, int steps = 1, Real minLength = 1e-5) +{ + const Real dt = mesh.getParent()->getDt(); + const Real str = min(dt * strength, (Real)1); + mesh.rebuildQuickCheck(); + + // calculate original mesh volume + Vec3 origCM; + Real origVolume = mesh.computeCenterOfMass(origCM); + + // temp vertices + const int numCorners = mesh.numTris() * 3; + const int numNodes = mesh.numNodes(); + vector<Vec3> temp(numNodes); + vector<bool> visited(numNodes); + + for (int s = 0; s < steps; s++) { + // reset markers + for (size_t i = 0; i < visited.size(); i++) + visited[i] = false; + + for (int c = 0; c < numCorners; c++) { + const int node = mesh.corners(c).node; + if (visited[node]) + continue; + + const Vec3 pos = mesh.nodes(node).pos; + Vec3 dx(0.0); + Real totalLen = 0; + + // rotate around vertex + set<int> &ring = mesh.get1Ring(node).nodes; + for (set<int>::iterator it = ring.begin(); it != ring.end(); it++) { + Vec3 edge = mesh.nodes(*it).pos - pos; + Real len = norm(edge); + + if (len > minLength) { + dx += edge * (1.0 / len); + totalLen += len; + } + else { + totalLen = 0.0; + break; + } + } + visited[node] = true; + temp[node] = pos; + if (totalLen != 0) + temp[node] += dx * (str / totalLen); + } + + // copy back + for (int n = 0; n < numNodes; n++) + if (!mesh.isNodeFixed(n)) + mesh.nodes(n).pos = temp[n]; + } + + // calculate new mesh volume + Vec3 newCM; + Real newVolume = mesh.computeCenterOfMass(newCM); + + // preserve volume : scale relative to CM + Real beta; +#if defined(WIN32) || defined(_WIN32) + beta = pow((Real)std::abs(origVolume / newVolume), (Real)(1. / 3.)); +#else + beta = cbrt(origVolume / newVolume); +#endif + + for (int n = 0; n < numNodes; n++) + if (!mesh.isNodeFixed(n)) + mesh.nodes(n).pos = origCM + (mesh.nodes(n).pos - newCM) * beta; +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "smoothMesh", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Mesh &mesh = *_args.getPtr<Mesh>("mesh", 0, &_lock); + Real strength = _args.get<Real>("strength", 1, &_lock); + int steps = _args.getOpt<int>("steps", 2, 1, &_lock); + Real minLength = _args.getOpt<Real>("minLength", 3, 1e-5, &_lock); + _retval = getPyNone(); + smoothMesh(mesh, strength, steps, minLength); + _args.check(); + } + pbFinalizePlugin(parent, "smoothMesh", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("smoothMesh", e.what()); + return 0; + } +} +static const Pb::Register _RP_smoothMesh("", "smoothMesh", _W_0); +extern "C" { +void PbRegister_smoothMesh() +{ + KEEP_UNUSED(_RP_smoothMesh); +} +} + +//! Subdivide and edgecollapse to guarantee mesh with edgelengths between +//! min/maxLength and an angle below minAngle +void subdivideMesh( + Mesh &mesh, Real minAngle, Real minLength, Real maxLength, bool cutTubes = false) +{ + // gather some statistics + int edgeSubdivs = 0, edgeCollsAngle = 0, edgeCollsLen = 0, edgeKill = 0; + mesh.rebuildQuickCheck(); + + vector<int> deletedNodes; + map<int, bool> taintedTris; + priority_queue<pair<Real, int>> pq; + + ////////////////////////////////////////// + // EDGE COLLAPSE // + // - particles marked for deletation // + ////////////////////////////////////////// + + for (int t = 0; t < mesh.numTris(); t++) { + if (taintedTris.find(t) != taintedTris.end()) + continue; + + // check if at least 2 nodes are marked for delete + bool k[3]; + int numKill = 0; + for (int i = 0; i < 3; i++) { + k[i] = mesh.nodes(mesh.tris(t).c[i]).flags & Mesh::NfKillme; + if (k[i]) + numKill++; + } + if (numKill < 2) + continue; + + if (k[0] && k[1]) + CollapseEdge(mesh, + t, + 2, + mesh.getEdge(t, 0), + mesh.getNode(t, 0), + deletedNodes, + taintedTris, + edgeKill, + cutTubes); + else if (k[1] && k[2]) + CollapseEdge(mesh, + t, + 0, + mesh.getEdge(t, 1), + mesh.getNode(t, 1), + deletedNodes, + taintedTris, + edgeKill, + cutTubes); + else if (k[2] && k[0]) + CollapseEdge(mesh, + t, + 1, + mesh.getEdge(t, 2), + mesh.getNode(t, 2), + deletedNodes, + taintedTris, + edgeKill, + cutTubes); + } + + ////////////////////////////////////////// + // EDGE COLLAPSING // + // - based on small triangle angle // + ////////////////////////////////////////// + + if (minAngle > 0) { + for (int t = 0; t < mesh.numTris(); t++) { + // we only want to run through the edge list ONCE. + // we achieve this in a method very similar to the above subdivision method. + + // if this triangle has already been deleted, ignore it + if (taintedTris.find(t) != taintedTris.end()) + continue; + + // first we find the angles of this triangle + Vec3 e0 = mesh.getEdge(t, 0), e1 = mesh.getEdge(t, 1), e2 = mesh.getEdge(t, 2); + Vec3 ne0 = e0; + Vec3 ne1 = e1; + Vec3 ne2 = e2; + normalize(ne0); + normalize(ne1); + normalize(ne2); + + // Real thisArea = sqrMag(cross(-e2,e0)); + // small angle approximation says sin(x) = arcsin(x) = x, + // arccos(x) = pi/2 - arcsin(x), + // cos(x) = dot(A,B), + // so angle is approximately 1 - dot(A,B). + Real angle[3]; + angle[0] = 1.0 - dot(ne0, -ne2); + angle[1] = 1.0 - dot(ne1, -ne0); + angle[2] = 1.0 - dot(ne2, -ne1); + Real worstAngle = angle[0]; + int which = 0; + if (angle[1] < worstAngle) { + worstAngle = angle[1]; + which = 1; + } + if (angle[2] < worstAngle) { + worstAngle = angle[2]; + which = 2; + } + + // then we see if the angle is too small + if (worstAngle < minAngle) { + Vec3 edgevect; + Vec3 endpoint; + switch (which) { + case 0: + endpoint = mesh.getNode(t, 1); + edgevect = e1; + break; + case 1: + endpoint = mesh.getNode(t, 2); + edgevect = e2; + break; + case 2: + endpoint = mesh.getNode(t, 0); + edgevect = e0; + break; + default: + break; + } + + CollapseEdge(mesh, + t, + which, + edgevect, + endpoint, + deletedNodes, + taintedTris, + edgeCollsAngle, + cutTubes); + } + } + } + + ////////////////////// + // EDGE SUBDIVISION // + ////////////////////// + + Real maxLength2 = maxLength * maxLength; + for (int t = 0; t < mesh.numTris(); t++) { + // first we find the maximum length edge in this triangle + Vec3 e0 = mesh.getEdge(t, 0), e1 = mesh.getEdge(t, 1), e2 = mesh.getEdge(t, 2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Real longest = max(d0, max(d1, d2)); + if (longest > maxLength2) { + pq.push(pair<Real, int>(longest, t)); + } + } + if (maxLength > 0) { + + while (!pq.empty() && pq.top().first > maxLength2) { + // we only want to run through the edge list ONCE + // and we want to subdivide the original edges before we subdivide any newer, shorter edges, + // so whenever we subdivide, we add the 2 new triangles on the end of the SurfaceTri vector + // and mark the original subdivided triangles for deletion. + // when we are done subdividing, we delete the obsolete triangles + + int triA = pq.top().second; + pq.pop(); + + if (taintedTris.find(triA) != taintedTris.end()) + continue; + + // first we find the maximum length edge in this triangle + Vec3 e0 = mesh.getEdge(triA, 0), e1 = mesh.getEdge(triA, 1), e2 = mesh.getEdge(triA, 2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Vec3 edgevect; + Vec3 endpoint; + int which; + if (d0 > d1) { + if (d0 > d2) { + edgevect = e0; + endpoint = mesh.getNode(triA, 0); + ; + which = 2; // 2 opposite of edge 0-1 + } + else { + edgevect = e2; + endpoint = mesh.getNode(triA, 2); + which = 1; // 1 opposite of edge 2-0 + } + } + else { + if (d1 > d2) { + edgevect = e1; + endpoint = mesh.getNode(triA, 1); + which = 0; // 0 opposite of edge 1-2 + } + else { + edgevect = e2; + endpoint = mesh.getNode(triA, 2); + which = 1; // 1 opposite of edge 2-0 + } + } + // This edge is too long, so we split it in the middle + + // * + // / \. + // /C0 \. + // / \. + // / \. + // / B \. + // / \. + // /C1 C2 \. + // *---------------* + // \C2 C1 / + // \ / + // \ A / + // \ / + // \ / + // \C0 / + // \ / + // * + // + // BECOMES + // + // * + // /|\. + // / | \. + // /C0|C0\. + // / | \. + // / B1 | B2 \. + // / | \. + // /C1 C2|C1 C2 \. + // *-------*-------* + // \C2 C1|C2 C1/ + // \ | / + // \ A2 | A1 / + // \ | / + // \C0|C0/ + // \ | / + // \|/ + // * + + int triB = -1; + bool haveB = false; + Corner ca_old[3], cb_old[3]; + ca_old[0] = mesh.corners(triA, which); + ca_old[1] = mesh.corners(ca_old[0].next); + ca_old[2] = mesh.corners(ca_old[0].prev); + if (ca_old[0].opposite >= 0) { + cb_old[0] = mesh.corners(ca_old[0].opposite); + cb_old[1] = mesh.corners(cb_old[0].next); + cb_old[2] = mesh.corners(cb_old[0].prev); + triB = cb_old[0].tri; + haveB = true; + } + // else throw Error("nonmanifold"); + + // subdivide in the middle of the edge and create new triangles + Node newNode; + newNode.flags = 0; + + newNode.pos = endpoint + 0.5 * edgevect; // fallback: linear average + // default: use butterfly + if (haveB) + newNode.pos = ModifiedButterflySubdivision(mesh, ca_old[0], cb_old[0], newNode.pos); + + // find indices of two points of 'which'-edge + // merge flags + int P0 = ca_old[1].node; + int P1 = ca_old[2].node; + newNode.flags = mesh.nodes(P0).flags | mesh.nodes(P1).flags; + + Real len0 = norm(mesh.nodes(P0).pos - newNode.pos); + Real len1 = norm(mesh.nodes(P1).pos - newNode.pos); + + // remove P0/P1 1-ring connection + mesh.get1Ring(P0).nodes.erase(P1); + mesh.get1Ring(P1).nodes.erase(P0); + mesh.get1Ring(P0).tris.erase(triA); + mesh.get1Ring(P1).tris.erase(triA); + mesh.get1Ring(ca_old[0].node).tris.erase(triA); + if (haveB) { + mesh.get1Ring(P0).tris.erase(triB); + mesh.get1Ring(P1).tris.erase(triB); + mesh.get1Ring(cb_old[0].node).tris.erase(triB); + } + + // init channel properties for new node + for (int i = 0; i < mesh.numNodeChannels(); i++) { + mesh.nodeChannel(i)->addInterpol(P0, P1, len0 / (len0 + len1)); + } + + // write to array + mesh.addTri(Triangle(ca_old[0].node, ca_old[1].node, mesh.numNodes())); + mesh.addTri(Triangle(ca_old[0].node, mesh.numNodes(), ca_old[2].node)); + if (haveB) { + mesh.addTri(Triangle(cb_old[0].node, cb_old[1].node, mesh.numNodes())); + mesh.addTri(Triangle(cb_old[0].node, mesh.numNodes(), cb_old[2].node)); + } + mesh.addNode(newNode); + + const int nt = haveB ? 4 : 2; + int triA1 = mesh.numTris() - nt; + int triA2 = mesh.numTris() - nt + 1; + int triB1 = 0, triB2 = 0; + if (haveB) { + triB1 = mesh.numTris() - nt + 2; + triB2 = mesh.numTris() - nt + 3; + } + mesh.tris(triA1).flags = mesh.tris(triA).flags; + mesh.tris(triA2).flags = mesh.tris(triA).flags; + mesh.tris(triB1).flags = mesh.tris(triB).flags; + mesh.tris(triB2).flags = mesh.tris(triB).flags; + + // connect new triangles to outside triangles, + // and connect outside triangles to these new ones + for (int c = 0; c < 3; c++) + mesh.addCorner(Corner(triA1, mesh.tris(triA1).c[c])); + for (int c = 0; c < 3; c++) + mesh.addCorner(Corner(triA2, mesh.tris(triA2).c[c])); + if (haveB) { + for (int c = 0; c < 3; c++) + mesh.addCorner(Corner(triB1, mesh.tris(triB1).c[c])); + for (int c = 0; c < 3; c++) + mesh.addCorner(Corner(triB2, mesh.tris(triB2).c[c])); + } + + int baseIdx = 3 * (mesh.numTris() - nt); + Corner *cBase = &mesh.corners(baseIdx); + + // set next/prev + for (int t = 0; t < nt; t++) + for (int c = 0; c < 3; c++) { + cBase[t * 3 + c].next = baseIdx + t * 3 + ((c + 1) % 3); + cBase[t * 3 + c].prev = baseIdx + t * 3 + ((c + 2) % 3); + } + + // set opposites + // A1 + cBase[0].opposite = haveB ? (baseIdx + 9) : -1; + cBase[1].opposite = baseIdx + 5; + cBase[2].opposite = -1; + if (ca_old[2].opposite >= 0) { + cBase[2].opposite = ca_old[2].opposite; + mesh.corners(cBase[2].opposite).opposite = baseIdx + 2; + } + // A2 + cBase[3].opposite = haveB ? (baseIdx + 6) : -1; + cBase[4].opposite = -1; + if (ca_old[1].opposite >= 0) { + cBase[4].opposite = ca_old[1].opposite; + mesh.corners(cBase[4].opposite).opposite = baseIdx + 4; + } + cBase[5].opposite = baseIdx + 1; + if (haveB) { + // B1 + cBase[6].opposite = baseIdx + 3; + cBase[7].opposite = baseIdx + 11; + cBase[8].opposite = -1; + if (cb_old[2].opposite >= 0) { + cBase[8].opposite = cb_old[2].opposite; + mesh.corners(cBase[8].opposite).opposite = baseIdx + 8; + } + // B2 + cBase[9].opposite = baseIdx + 0; + cBase[10].opposite = -1; + if (cb_old[1].opposite >= 0) { + cBase[10].opposite = cb_old[1].opposite; + mesh.corners(cBase[10].opposite).opposite = baseIdx + 10; + } + cBase[11].opposite = baseIdx + 7; + } + + //////////////////// + // mark the two original triangles for deletion + taintedTris[triA] = true; + mesh.removeTriFromLookup(triA); + if (haveB) { + taintedTris[triB] = true; + mesh.removeTriFromLookup(triB); + } + + Real areaA1 = mesh.getFaceArea(triA1), areaA2 = mesh.getFaceArea(triA2); + Real areaB1 = 0, areaB2 = 0; + if (haveB) { + areaB1 = mesh.getFaceArea(triB1); + areaB2 = mesh.getFaceArea(triB2); + } + + // add channel props for new triangles + for (int i = 0; i < mesh.numTriChannels(); i++) { + mesh.triChannel(i)->addSplit(triA, areaA1 / (areaA1 + areaA2)); + mesh.triChannel(i)->addSplit(triA, areaA2 / (areaA1 + areaA2)); + if (haveB) { + mesh.triChannel(i)->addSplit(triB, areaB1 / (areaB1 + areaB2)); + mesh.triChannel(i)->addSplit(triB, areaB2 / (areaB1 + areaB2)); + } + } + + // add the four new triangles to the prority queue + for (int i = mesh.numTris() - nt; i < mesh.numTris(); i++) { + // find the maximum length edge in this triangle + Vec3 ne0 = mesh.getEdge(i, 0), ne1 = mesh.getEdge(i, 1), ne2 = mesh.getEdge(i, 2); + Real nd0 = normSquare(ne0); + Real nd1 = normSquare(ne1); + Real nd2 = normSquare(ne2); + Real longest = max(nd0, max(nd1, nd2)); + // longest = (int)(longest * 1e2) / 1e2; // HACK: truncate + pq.push(pair<Real, int>(longest, i)); + } + edgeSubdivs++; + } + } + + ////////////////////////////////////////// + // EDGE COLLAPSING // + // - based on short edge length // + ////////////////////////////////////////// + if (minLength > 0) { + const Real minLength2 = minLength * minLength; + for (int t = 0; t < mesh.numTris(); t++) { + // we only want to run through the edge list ONCE. + // we achieve this in a method very similar to the above subdivision method. + + // NOTE: + // priority queue does not work so great in the edge collapse case, + // because collapsing one triangle affects the edge lengths + // of many neighbor triangles, + // and we do not update their maximum edge length in the queue. + + // if this triangle has already been deleted, ignore it + // if(taintedTris[t]) + // continue; + + if (taintedTris.find(t) != taintedTris.end()) + continue; + + // first we find the minimum length edge in this triangle + Vec3 e0 = mesh.getEdge(t, 0), e1 = mesh.getEdge(t, 1), e2 = mesh.getEdge(t, 2); + Real d0 = normSquare(e0); + Real d1 = normSquare(e1); + Real d2 = normSquare(e2); + + Vec3 edgevect; + Vec3 endpoint; + Real dist2; + int which; + if (d0 < d1) { + if (d0 < d2) { + dist2 = d0; + edgevect = e0; + endpoint = mesh.getNode(t, 0); + which = 2; // 2 opposite of edge 0-1 + } + else { + dist2 = d2; + edgevect = e2; + endpoint = mesh.getNode(t, 2); + which = 1; // 1 opposite of edge 2-0 + } + } + else { + if (d1 < d2) { + dist2 = d1; + edgevect = e1; + endpoint = mesh.getNode(t, 1); + which = 0; // 0 opposite of edge 1-2 + } + else { + dist2 = d2; + edgevect = e2; + endpoint = mesh.getNode(t, 2); + which = 1; // 1 opposite of edge 2-0 + } + } + // then we see if the min length edge is too short + if (dist2 < minLength2) { + CollapseEdge( + mesh, t, which, edgevect, endpoint, deletedNodes, taintedTris, edgeCollsLen, cutTubes); + } + } + } + // cleanup nodes and triangles marked for deletion + + // we run backwards through the deleted array, + // replacing triangles with ones from the back + // (this avoids the potential problem of overwriting a triangle + // with a to-be-deleted triangle) + std::map<int, bool>::reverse_iterator tti = taintedTris.rbegin(); + for (; tti != taintedTris.rend(); tti++) + mesh.removeTri(tti->first); + + mesh.removeNodes(deletedNodes); + cout << "Surface subdivision finished with " << mesh.numNodes() << " surface nodes and " + << mesh.numTris(); + cout << " surface triangles, edgeSubdivs:" << edgeSubdivs << ", edgeCollapses: " << edgeCollsLen; + cout << " + " << edgeCollsAngle << " + " << edgeKill << endl; + // mesh.sanityCheck(); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "subdivideMesh", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Mesh &mesh = *_args.getPtr<Mesh>("mesh", 0, &_lock); + Real minAngle = _args.get<Real>("minAngle", 1, &_lock); + Real minLength = _args.get<Real>("minLength", 2, &_lock); + Real maxLength = _args.get<Real>("maxLength", 3, &_lock); + bool cutTubes = _args.getOpt<bool>("cutTubes", 4, false, &_lock); + _retval = getPyNone(); + subdivideMesh(mesh, minAngle, minLength, maxLength, cutTubes); + _args.check(); + } + pbFinalizePlugin(parent, "subdivideMesh", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("subdivideMesh", e.what()); + return 0; + } +} +static const Pb::Register _RP_subdivideMesh("", "subdivideMesh", _W_1); +extern "C" { +void PbRegister_subdivideMesh() +{ + KEEP_UNUSED(_RP_subdivideMesh); +} +} + +void killSmallComponents(Mesh &mesh, int elements = 10) +{ + const int num = mesh.numTris(); + vector<int> comp(num); + vector<int> numEl; + vector<int> deletedNodes; + vector<bool> isNodeDel(mesh.numNodes()); + map<int, bool> taintedTris; + // enumerate components + int cur = 0; + for (int i = 0; i < num; i++) { + if (comp[i] == 0) { + cur++; + comp[i] = cur; + + stack<int> stack; + stack.push(i); + int cnt = 1; + while (!stack.empty()) { + int tri = stack.top(); + stack.pop(); + for (int c = 0; c < 3; c++) { + int op = mesh.corners(tri, c).opposite; + if (op < 0) + continue; + int ntri = mesh.corners(op).tri; + if (comp[ntri] == 0) { + comp[ntri] = cur; + stack.push(ntri); + cnt++; + } + } + } + numEl.push_back(cnt); + } + } + // kill small components + for (int j = 0; j < num; j++) { + if (numEl[comp[j] - 1] < elements) { + taintedTris[j] = true; + for (int c = 0; c < 3; c++) { + int n = mesh.tris(j).c[c]; + if (!isNodeDel[n]) { + isNodeDel[n] = true; + deletedNodes.push_back(n); + } + } + } + } + + std::map<int, bool>::reverse_iterator tti = taintedTris.rbegin(); + for (; tti != taintedTris.rend(); tti++) + mesh.removeTri(tti->first); + + mesh.removeNodes(deletedNodes); + + if (!taintedTris.empty()) + cout << "Killed small components : " << deletedNodes.size() << " nodes, " << taintedTris.size() + << " tris deleted." << endl; +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "killSmallComponents", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Mesh &mesh = *_args.getPtr<Mesh>("mesh", 0, &_lock); + int elements = _args.getOpt<int>("elements", 1, 10, &_lock); + _retval = getPyNone(); + killSmallComponents(mesh, elements); + _args.check(); + } + pbFinalizePlugin(parent, "killSmallComponents", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("killSmallComponents", e.what()); + return 0; + } +} +static const Pb::Register _RP_killSmallComponents("", "killSmallComponents", _W_2); +extern "C" { +void PbRegister_killSmallComponents() +{ + KEEP_UNUSED(_RP_killSmallComponents); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/pressure.cpp b/extern/mantaflow/preprocessed/plugin/pressure.cpp new file mode 100644 index 00000000000..7def2669e36 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/pressure.cpp @@ -0,0 +1,1511 @@ + + +// 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 pressure correction: solve_pressure, and ghost fluid helpers + * + ******************************************************************************/ +#include "vectorbase.h" +#include "kernel.h" +#include "conjugategrad.h" +#include "multigrid.h" + +using namespace std; +namespace Manta { + +//! Preconditioner for CG solver +// - None: Use standard CG +// - MIC: Modified incomplete Cholesky preconditioner +// - MGDynamic: Multigrid preconditioner, rebuilt for each solve +// - MGStatic: Multigrid preconditioner, built only once (faster than +// MGDynamic, but works only if Poisson equation does not change) +enum Preconditioner { PcNone = 0, PcMIC = 1, PcMGDynamic = 2, PcMGStatic = 3 }; + +inline static Real surfTensHelper(const IndexInt idx, + const int offset, + const Grid<Real> &phi, + const Grid<Real> &curv, + const Real surfTens, + const Real gfClamp); + +//! Kernel: Construct the right-hand side of the poisson equation + +struct MakeRhs : public KernelBase { + MakeRhs(const FlagGrid &flags, + Grid<Real> &rhs, + const MACGrid &vel, + const Grid<Real> *perCellCorr, + const MACGrid *fractions, + const MACGrid *obvel, + const Grid<Real> *phi, + const Grid<Real> *curv, + const Real surfTens, + const Real gfClamp) + : KernelBase(&flags, 1), + flags(flags), + rhs(rhs), + vel(vel), + perCellCorr(perCellCorr), + fractions(fractions), + obvel(obvel), + phi(phi), + curv(curv), + surfTens(surfTens), + gfClamp(gfClamp), + cnt(0), + sum(0) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &rhs, + const MACGrid &vel, + const Grid<Real> *perCellCorr, + const MACGrid *fractions, + const MACGrid *obvel, + const Grid<Real> *phi, + const Grid<Real> *curv, + const Real surfTens, + const Real gfClamp, + int &cnt, + double &sum) + { + if (!flags.isFluid(i, j, k)) { + rhs(i, j, k) = 0; + return; + } + + // compute negative divergence + // no flag checks: assumes vel at obstacle interfaces is set to zero + Real set(0); + if (!fractions) { + set = vel(i, j, k).x - vel(i + 1, j, k).x + vel(i, j, k).y - vel(i, j + 1, k).y; + if (vel.is3D()) + set += vel(i, j, k).z - vel(i, j, k + 1).z; + } + else { + set = (*fractions)(i, j, k).x * vel(i, j, k).x - + (*fractions)(i + 1, j, k).x * vel(i + 1, j, k).x + + (*fractions)(i, j, k).y * vel(i, j, k).y - + (*fractions)(i, j + 1, k).y * vel(i, j + 1, k).y; + if (vel.is3D()) + set += (*fractions)(i, j, k).z * vel(i, j, k).z - + (*fractions)(i, j, k + 1).z * vel(i, j, k + 1).z; + + // compute divergence from obstacle by using obstacle velocity (optional) + if (obvel) { + set += (1 - (*fractions)(i, j, k).x) * (*obvel)(i, j, k).x - + (1 - (*fractions)(i + 1, j, k).x) * (*obvel)(i + 1, j, k).x + + (1 - (*fractions)(i, j, k).y) * (*obvel)(i, j, k).y - + (1 - (*fractions)(i, j + 1, k).y) * (*obvel)(i, j + 1, k).y; + if (obvel->is3D()) + set += (1 - (*fractions)(i, j, k).z) * (*obvel)(i, j, k).z - + (1 - (*fractions)(i, j, k + 1).z) * (*obvel)(i, j, k + 1).z; + } + } + + // compute surface tension effect (optional) + if (phi && curv) { + const IndexInt idx = flags.index(i, j, k); + const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ(); + if (flags.isEmpty(i - 1, j, k)) + set += surfTensHelper(idx, -X, *phi, *curv, surfTens, gfClamp); + if (flags.isEmpty(i + 1, j, k)) + set += surfTensHelper(idx, +X, *phi, *curv, surfTens, gfClamp); + if (flags.isEmpty(i, j - 1, k)) + set += surfTensHelper(idx, -Y, *phi, *curv, surfTens, gfClamp); + if (flags.isEmpty(i, j + 1, k)) + set += surfTensHelper(idx, +Y, *phi, *curv, surfTens, gfClamp); + if (vel.is3D()) { + if (flags.isEmpty(i, j, k - 1)) + set += surfTensHelper(idx, -Z, *phi, *curv, surfTens, gfClamp); + if (flags.isEmpty(i, j, k + 1)) + set += surfTensHelper(idx, +Z, *phi, *curv, surfTens, gfClamp); + } + } + + // per cell divergence correction (optional) + if (perCellCorr) + set += perCellCorr->get(i, j, k); + + // obtain sum, cell count + sum += set; + cnt++; + + rhs(i, j, k) = set; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return rhs; + } + typedef Grid<Real> type1; + inline const MACGrid &getArg2() + { + return vel; + } + typedef MACGrid type2; + inline const Grid<Real> *getArg3() + { + return perCellCorr; + } + typedef Grid<Real> type3; + inline const MACGrid *getArg4() + { + return fractions; + } + typedef MACGrid type4; + inline const MACGrid *getArg5() + { + return obvel; + } + typedef MACGrid type5; + inline const Grid<Real> *getArg6() + { + return phi; + } + typedef Grid<Real> type6; + inline const Grid<Real> *getArg7() + { + return curv; + } + typedef Grid<Real> type7; + inline const Real &getArg8() + { + return surfTens; + } + typedef Real type8; + inline const Real &getArg9() + { + return gfClamp; + } + typedef Real type9; + void runMessage() + { + debMsg("Executing kernel MakeRhs ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, + j, + k, + flags, + rhs, + vel, + perCellCorr, + fractions, + obvel, + phi, + curv, + surfTens, + gfClamp, + cnt, + sum); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, + j, + k, + flags, + rhs, + vel, + perCellCorr, + fractions, + obvel, + phi, + curv, + surfTens, + gfClamp, + cnt, + sum); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_reduce(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_reduce(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + MakeRhs(MakeRhs &o, tbb::split) + : KernelBase(o), + flags(o.flags), + rhs(o.rhs), + vel(o.vel), + perCellCorr(o.perCellCorr), + fractions(o.fractions), + obvel(o.obvel), + phi(o.phi), + curv(o.curv), + surfTens(o.surfTens), + gfClamp(o.gfClamp), + cnt(0), + sum(0) + { + } + void join(const MakeRhs &o) + { + cnt += o.cnt; + sum += o.sum; + } + const FlagGrid &flags; + Grid<Real> &rhs; + const MACGrid &vel; + const Grid<Real> *perCellCorr; + const MACGrid *fractions; + const MACGrid *obvel; + const Grid<Real> *phi; + const Grid<Real> *curv; + const Real surfTens; + const Real gfClamp; + int cnt; + double sum; +}; + +//! Kernel: make velocity divergence free by subtracting pressure gradient + +struct knCorrectVelocity : public KernelBase { + knCorrectVelocity(const FlagGrid &flags, MACGrid &vel, const Grid<Real> &pressure) + : KernelBase(&flags, 1), flags(flags), vel(vel), pressure(pressure) + { + runMessage(); + run(); + } + inline void op( + int i, int j, int k, const FlagGrid &flags, MACGrid &vel, const Grid<Real> &pressure) const + { + const IndexInt idx = flags.index(i, j, k); + if (flags.isFluid(idx)) { + if (flags.isFluid(i - 1, j, k)) + vel[idx].x -= (pressure[idx] - pressure(i - 1, j, k)); + if (flags.isFluid(i, j - 1, k)) + vel[idx].y -= (pressure[idx] - pressure(i, j - 1, k)); + if (flags.is3D() && flags.isFluid(i, j, k - 1)) + vel[idx].z -= (pressure[idx] - pressure(i, j, k - 1)); + + if (flags.isEmpty(i - 1, j, k)) + vel[idx].x -= pressure[idx]; + if (flags.isEmpty(i, j - 1, k)) + vel[idx].y -= pressure[idx]; + if (flags.is3D() && flags.isEmpty(i, j, k - 1)) + vel[idx].z -= pressure[idx]; + } + else if (flags.isEmpty(idx) && + !flags.isOutflow(idx)) { // don't change velocities in outflow cells + if (flags.isFluid(i - 1, j, k)) + vel[idx].x += pressure(i - 1, j, k); + else + vel[idx].x = 0.f; + if (flags.isFluid(i, j - 1, k)) + vel[idx].y += pressure(i, j - 1, k); + else + vel[idx].y = 0.f; + if (flags.is3D()) { + if (flags.isFluid(i, j, k - 1)) + vel[idx].z += pressure(i, j, k - 1); + else + vel[idx].z = 0.f; + } + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline const Grid<Real> &getArg2() + { + return pressure; + } + typedef Grid<Real> type2; + void runMessage() + { + debMsg("Executing kernel knCorrectVelocity ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, pressure); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, vel, pressure); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + MACGrid &vel; + const Grid<Real> &pressure; +}; + +// ***************************************************************************** +// Ghost fluid helpers + +// calculate fraction filled with liquid (note, assumes inside value is < outside!) +inline static Real thetaHelper(const Real inside, const Real outside) +{ + const Real denom = inside - outside; + if (denom > -1e-04) + return 0.5; // should always be neg, and large enough... + return std::max(Real(0), std::min(Real(1), inside / denom)); +} + +// calculate ghost fluid factor, cell at idx should be a fluid cell +inline static Real ghostFluidHelper(const IndexInt idx, + const int offset, + const Grid<Real> &phi, + const Real gfClamp) +{ + Real alpha = thetaHelper(phi[idx], phi[idx + offset]); + if (alpha < gfClamp) + return alpha = gfClamp; + return (1. - (1. / alpha)); +} + +inline static Real surfTensHelper(const IndexInt idx, + const int offset, + const Grid<Real> &phi, + const Grid<Real> &curv, + const Real surfTens, + const Real gfClamp) +{ + return surfTens * (curv[idx + offset] - ghostFluidHelper(idx, offset, phi, gfClamp) * curv[idx]); +} + +//! Kernel: Adapt A0 for ghost fluid + +struct ApplyGhostFluidDiagonal : public KernelBase { + ApplyGhostFluidDiagonal(Grid<Real> &A0, + const FlagGrid &flags, + const Grid<Real> &phi, + const Real gfClamp) + : KernelBase(&A0, 1), A0(A0), flags(flags), phi(phi), gfClamp(gfClamp) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<Real> &A0, + const FlagGrid &flags, + const Grid<Real> &phi, + const Real gfClamp) const + { + const int X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ(); + const IndexInt idx = flags.index(i, j, k); + if (!flags.isFluid(idx)) + return; + + if (flags.isEmpty(i - 1, j, k)) + A0[idx] -= ghostFluidHelper(idx, -X, phi, gfClamp); + if (flags.isEmpty(i + 1, j, k)) + A0[idx] -= ghostFluidHelper(idx, +X, phi, gfClamp); + if (flags.isEmpty(i, j - 1, k)) + A0[idx] -= ghostFluidHelper(idx, -Y, phi, gfClamp); + if (flags.isEmpty(i, j + 1, k)) + A0[idx] -= ghostFluidHelper(idx, +Y, phi, gfClamp); + if (flags.is3D()) { + if (flags.isEmpty(i, j, k - 1)) + A0[idx] -= ghostFluidHelper(idx, -Z, phi, gfClamp); + if (flags.isEmpty(i, j, k + 1)) + A0[idx] -= ghostFluidHelper(idx, +Z, phi, gfClamp); + } + } + inline Grid<Real> &getArg0() + { + return A0; + } + typedef Grid<Real> type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const Grid<Real> &getArg2() + { + return phi; + } + typedef Grid<Real> type2; + inline const Real &getArg3() + { + return gfClamp; + } + typedef Real type3; + void runMessage() + { + debMsg("Executing kernel ApplyGhostFluidDiagonal ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, A0, flags, phi, gfClamp); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, A0, flags, phi, gfClamp); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + Grid<Real> &A0; + const FlagGrid &flags; + const Grid<Real> φ + const Real gfClamp; +}; + +//! Kernel: Apply velocity update: ghost fluid contribution + +struct knCorrectVelocityGhostFluid : public KernelBase { + knCorrectVelocityGhostFluid(MACGrid &vel, + const FlagGrid &flags, + const Grid<Real> &pressure, + const Grid<Real> &phi, + Real gfClamp, + const Grid<Real> *curv, + const Real surfTens) + : KernelBase(&vel, 1), + vel(vel), + flags(flags), + pressure(pressure), + phi(phi), + gfClamp(gfClamp), + curv(curv), + surfTens(surfTens) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + MACGrid &vel, + const FlagGrid &flags, + const Grid<Real> &pressure, + const Grid<Real> &phi, + Real gfClamp, + const Grid<Real> *curv, + const Real surfTens) const + { + const IndexInt X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ(); + const IndexInt idx = flags.index(i, j, k); + if (flags.isFluid(idx)) { + if (flags.isEmpty(i - 1, j, k)) + vel[idx][0] += pressure[idx] * ghostFluidHelper(idx, -X, phi, gfClamp); + if (flags.isEmpty(i, j - 1, k)) + vel[idx][1] += pressure[idx] * ghostFluidHelper(idx, -Y, phi, gfClamp); + if (flags.is3D() && flags.isEmpty(i, j, k - 1)) + vel[idx][2] += pressure[idx] * ghostFluidHelper(idx, -Z, phi, gfClamp); + } + else if (flags.isEmpty(idx) && + !flags.isOutflow(idx)) { // do not change velocities in outflow cells + if (flags.isFluid(i - 1, j, k)) + vel[idx][0] -= pressure(i - 1, j, k) * ghostFluidHelper(idx - X, +X, phi, gfClamp); + else + vel[idx].x = 0.f; + if (flags.isFluid(i, j - 1, k)) + vel[idx][1] -= pressure(i, j - 1, k) * ghostFluidHelper(idx - Y, +Y, phi, gfClamp); + else + vel[idx].y = 0.f; + if (flags.is3D()) { + if (flags.isFluid(i, j, k - 1)) + vel[idx][2] -= pressure(i, j, k - 1) * ghostFluidHelper(idx - Z, +Z, phi, gfClamp); + else + vel[idx].z = 0.f; + } + } + + if (curv) { + if (flags.isFluid(idx)) { + if (flags.isEmpty(i - 1, j, k)) + vel[idx].x += surfTensHelper(idx, -X, phi, *curv, surfTens, gfClamp); + if (flags.isEmpty(i, j - 1, k)) + vel[idx].y += surfTensHelper(idx, -Y, phi, *curv, surfTens, gfClamp); + if (flags.is3D() && flags.isEmpty(i, j, k - 1)) + vel[idx].z += surfTensHelper(idx, -Z, phi, *curv, surfTens, gfClamp); + } + else if (flags.isEmpty(idx) && + !flags.isOutflow(idx)) { // do not change velocities in outflow cells + vel[idx].x -= (flags.isFluid(i - 1, j, k)) ? + surfTensHelper(idx - X, +X, phi, *curv, surfTens, gfClamp) : + 0.f; + vel[idx].y -= (flags.isFluid(i, j - 1, k)) ? + surfTensHelper(idx - Y, +Y, phi, *curv, surfTens, gfClamp) : + 0.f; + if (flags.is3D()) + vel[idx].z -= (flags.isFluid(i, j, k - 1)) ? + surfTensHelper(idx - Z, +Z, phi, *curv, surfTens, gfClamp) : + 0.f; + } + } + } + inline MACGrid &getArg0() + { + return vel; + } + typedef MACGrid type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const Grid<Real> &getArg2() + { + return pressure; + } + typedef Grid<Real> type2; + inline const Grid<Real> &getArg3() + { + return phi; + } + typedef Grid<Real> type3; + inline Real &getArg4() + { + return gfClamp; + } + typedef Real type4; + inline const Grid<Real> *getArg5() + { + return curv; + } + typedef Grid<Real> type5; + inline const Real &getArg6() + { + return surfTens; + } + typedef Real type6; + void runMessage() + { + debMsg("Executing kernel knCorrectVelocityGhostFluid ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, flags, pressure, phi, gfClamp, curv, surfTens); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, flags, pressure, phi, gfClamp, curv, surfTens); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + MACGrid &vel; + const FlagGrid &flags; + const Grid<Real> &pressure; + const Grid<Real> φ + Real gfClamp; + const Grid<Real> *curv; + const Real surfTens; +}; + +// improve behavior of clamping for large time steps: +inline static Real ghostFluidWasClamped(const IndexInt idx, + const int offset, + const Grid<Real> &phi, + const Real gfClamp) +{ + const Real alpha = thetaHelper(phi[idx], phi[idx + offset]); + if (alpha < gfClamp) + return true; + return false; +} + +struct knReplaceClampedGhostFluidVels : public KernelBase { + knReplaceClampedGhostFluidVels(MACGrid &vel, + const FlagGrid &flags, + const Grid<Real> &pressure, + const Grid<Real> &phi, + Real gfClamp) + : KernelBase(&vel, 1), vel(vel), flags(flags), pressure(pressure), phi(phi), gfClamp(gfClamp) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + MACGrid &vel, + const FlagGrid &flags, + const Grid<Real> &pressure, + const Grid<Real> &phi, + Real gfClamp) const + { + const IndexInt idx = flags.index(i, j, k); + const IndexInt X = flags.getStrideX(), Y = flags.getStrideY(), Z = flags.getStrideZ(); + if (!flags.isEmpty(idx)) + return; + + if (flags.isFluid(i - 1, j, k) && ghostFluidWasClamped(idx - X, +X, phi, gfClamp)) + vel[idx][0] = vel[idx - X][0]; + if (flags.isFluid(i, j - 1, k) && ghostFluidWasClamped(idx - Y, +Y, phi, gfClamp)) + vel[idx][1] = vel[idx - Y][1]; + if (flags.is3D() && flags.isFluid(i, j, k - 1) && + ghostFluidWasClamped(idx - Z, +Z, phi, gfClamp)) + vel[idx][2] = vel[idx - Z][2]; + + if (flags.isFluid(i + 1, j, k) && ghostFluidWasClamped(idx + X, -X, phi, gfClamp)) + vel[idx][0] = vel[idx + X][0]; + if (flags.isFluid(i, j + 1, k) && ghostFluidWasClamped(idx + Y, -Y, phi, gfClamp)) + vel[idx][1] = vel[idx + Y][1]; + if (flags.is3D() && flags.isFluid(i, j, k + 1) && + ghostFluidWasClamped(idx + Z, -Z, phi, gfClamp)) + vel[idx][2] = vel[idx + Z][2]; + } + inline MACGrid &getArg0() + { + return vel; + } + typedef MACGrid type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const Grid<Real> &getArg2() + { + return pressure; + } + typedef Grid<Real> type2; + inline const Grid<Real> &getArg3() + { + return phi; + } + typedef Grid<Real> type3; + inline Real &getArg4() + { + return gfClamp; + } + typedef Real type4; + void runMessage() + { + debMsg("Executing kernel knReplaceClampedGhostFluidVels ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, flags, pressure, phi, gfClamp); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, flags, pressure, phi, gfClamp); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + MACGrid &vel; + const FlagGrid &flags; + const Grid<Real> &pressure; + const Grid<Real> φ + Real gfClamp; +}; + +//! Kernel: Compute min value of Real grid + +struct CountEmptyCells : public KernelBase { + CountEmptyCells(const FlagGrid &flags) : KernelBase(&flags, 0), flags(flags), numEmpty(0) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, const FlagGrid &flags, int &numEmpty) + { + if (flags.isEmpty(idx)) + numEmpty++; + } + inline operator int() + { + return numEmpty; + } + inline int &getRet() + { + return numEmpty; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + void runMessage() + { + debMsg("Executing kernel CountEmptyCells ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, flags, numEmpty); + } + void run() + { + tbb::parallel_reduce(tbb::blocked_range<IndexInt>(0, size), *this); + } + CountEmptyCells(CountEmptyCells &o, tbb::split) : KernelBase(o), flags(o.flags), numEmpty(0) + { + } + void join(const CountEmptyCells &o) + { + numEmpty += o.numEmpty; + } + const FlagGrid &flags; + int numEmpty; +}; + +// ***************************************************************************** +// Misc helpers + +//! Change 'A' and 'rhs' such that pressure at 'fixPidx' is fixed to 'value' +void fixPressure(int fixPidx, + Real value, + Grid<Real> &rhs, + Grid<Real> &A0, + Grid<Real> &Ai, + Grid<Real> &Aj, + Grid<Real> &Ak) +{ + // Bring to rhs at neighbors + rhs[fixPidx + Ai.getStrideX()] -= Ai[fixPidx] * value; + rhs[fixPidx + Aj.getStrideY()] -= Aj[fixPidx] * value; + rhs[fixPidx - Ai.getStrideX()] -= Ai[fixPidx - Ai.getStrideX()] * value; + rhs[fixPidx - Aj.getStrideY()] -= Aj[fixPidx - Aj.getStrideY()] * value; + if (rhs.is3D()) { + rhs[fixPidx + Ak.getStrideZ()] -= Ak[fixPidx] * value; + rhs[fixPidx - Ak.getStrideZ()] -= Ak[fixPidx - Ak.getStrideZ()] * value; + } + + // Trivialize equation at 'fixPidx' to: pressure[fixPidx] = value + rhs[fixPidx] = value; + A0[fixPidx] = Real(1); + Ai[fixPidx] = Aj[fixPidx] = Ak[fixPidx] = Real(0); + Ai[fixPidx - Ai.getStrideX()] = Real(0); + Aj[fixPidx - Aj.getStrideY()] = Real(0); + if (rhs.is3D()) { + Ak[fixPidx - Ak.getStrideZ()] = Real(0); + } +} + +// for "static" MG mode, keep one MG data structure per fluid solver +// leave cleanup to OS/user if nonzero at program termination (PcMGStatic mode) +// alternatively, manually release in scene file with releaseMG +static std::map<FluidSolver *, GridMg *> gMapMG; + +void releaseMG(FluidSolver *solver = nullptr) +{ + // release all? + if (!solver) { + for (std::map<FluidSolver *, GridMg *>::iterator it = gMapMG.begin(); it != gMapMG.end(); + it++) { + if (it->first != nullptr) + releaseMG(it->first); + } + return; + } + + GridMg *mg = gMapMG[solver]; + if (mg) { + delete mg; + gMapMG[solver] = nullptr; + } +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "releaseMG", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + FluidSolver *solver = _args.getPtrOpt<FluidSolver>("solver", 0, nullptr, &_lock); + _retval = getPyNone(); + releaseMG(solver); + _args.check(); + } + pbFinalizePlugin(parent, "releaseMG", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("releaseMG", e.what()); + return 0; + } +} +static const Pb::Register _RP_releaseMG("", "releaseMG", _W_0); +extern "C" { +void PbRegister_releaseMG() +{ + KEEP_UNUSED(_RP_releaseMG); +} +} + +// ***************************************************************************** +// Main pressure solve + +// Note , all three pressure solve helper functions take +// identical parameters, apart from the RHS grid (and different const values) + +//! Compute rhs for pressure solve + +void computePressureRhs(Grid<Real> &rhs, + const MACGrid &vel, + const Grid<Real> &pressure, + const FlagGrid &flags, + Real cgAccuracy = 1e-3, + const Grid<Real> *phi = 0, + const Grid<Real> *perCellCorr = 0, + const MACGrid *fractions = 0, + const MACGrid *obvel = 0, + Real gfClamp = 1e-04, + Real cgMaxIterFac = 1.5, + bool precondition = true, + int preconditioner = PcMIC, + bool enforceCompatibility = false, + bool useL2Norm = false, + bool zeroPressureFixing = false, + const Grid<Real> *curv = NULL, + const Real surfTens = 0.) +{ + // compute divergence and init right hand side + MakeRhs kernMakeRhs( + flags, rhs, vel, perCellCorr, fractions, obvel, phi, curv, surfTens, gfClamp); + + if (enforceCompatibility) + rhs += (Real)(-kernMakeRhs.sum / (Real)kernMakeRhs.cnt); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "computePressureRhs", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &rhs = *_args.getPtr<Grid<Real>>("rhs", 0, &_lock); + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + const Grid<Real> &pressure = *_args.getPtr<Grid<Real>>("pressure", 2, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 3, &_lock); + Real cgAccuracy = _args.getOpt<Real>("cgAccuracy", 4, 1e-3, &_lock); + const Grid<Real> *phi = _args.getPtrOpt<Grid<Real>>("phi", 5, 0, &_lock); + const Grid<Real> *perCellCorr = _args.getPtrOpt<Grid<Real>>("perCellCorr", 6, 0, &_lock); + const MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 7, 0, &_lock); + const MACGrid *obvel = _args.getPtrOpt<MACGrid>("obvel", 8, 0, &_lock); + Real gfClamp = _args.getOpt<Real>("gfClamp", 9, 1e-04, &_lock); + Real cgMaxIterFac = _args.getOpt<Real>("cgMaxIterFac", 10, 1.5, &_lock); + bool precondition = _args.getOpt<bool>("precondition", 11, true, &_lock); + int preconditioner = _args.getOpt<int>("preconditioner", 12, PcMIC, &_lock); + bool enforceCompatibility = _args.getOpt<bool>("enforceCompatibility", 13, false, &_lock); + bool useL2Norm = _args.getOpt<bool>("useL2Norm", 14, false, &_lock); + bool zeroPressureFixing = _args.getOpt<bool>("zeroPressureFixing", 15, false, &_lock); + const Grid<Real> *curv = _args.getPtrOpt<Grid<Real>>("curv", 16, NULL, &_lock); + const Real surfTens = _args.getOpt<Real>("surfTens", 17, 0., &_lock); + _retval = getPyNone(); + computePressureRhs(rhs, + vel, + pressure, + flags, + cgAccuracy, + phi, + perCellCorr, + fractions, + obvel, + gfClamp, + cgMaxIterFac, + precondition, + preconditioner, + enforceCompatibility, + useL2Norm, + zeroPressureFixing, + curv, + surfTens); + _args.check(); + } + pbFinalizePlugin(parent, "computePressureRhs", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("computePressureRhs", e.what()); + return 0; + } +} +static const Pb::Register _RP_computePressureRhs("", "computePressureRhs", _W_1); +extern "C" { +void PbRegister_computePressureRhs() +{ + KEEP_UNUSED(_RP_computePressureRhs); +} +} + +//! Build and solve pressure system of equations +//! perCellCorr: a divergence correction for each cell, optional +//! fractions: for 2nd order obstacle boundaries, optional +//! gfClamp: clamping threshold for ghost fluid method +//! cgMaxIterFac: heuristic to determine maximal number of CG iteations, increase for more accurate +//! solutions preconditioner: MIC, or MG (see Preconditioner enum) useL2Norm: use max norm by +//! default, can be turned to L2 here zeroPressureFixing: remove null space by fixing a single +//! pressure value, needed for MG curv: curvature for surface tension effects surfTens: surface +//! tension coefficient retRhs: return RHS divergence, e.g., for debugging; optional + +void solvePressureSystem(Grid<Real> &rhs, + MACGrid &vel, + Grid<Real> &pressure, + const FlagGrid &flags, + Real cgAccuracy = 1e-3, + const Grid<Real> *phi = 0, + const Grid<Real> *perCellCorr = 0, + const MACGrid *fractions = 0, + Real gfClamp = 1e-04, + Real cgMaxIterFac = 1.5, + bool precondition = true, + int preconditioner = PcMIC, + const bool enforceCompatibility = false, + const bool useL2Norm = false, + const bool zeroPressureFixing = false, + const Grid<Real> *curv = NULL, + const Real surfTens = 0.) +{ + if (precondition == false) + preconditioner = PcNone; // for backwards compatibility + + // reserve temp grids + FluidSolver *parent = flags.getParent(); + Grid<Real> residual(parent); + Grid<Real> search(parent); + Grid<Real> A0(parent); + Grid<Real> Ai(parent); + Grid<Real> Aj(parent); + Grid<Real> Ak(parent); + Grid<Real> tmp(parent); + + // setup matrix and boundaries + MakeLaplaceMatrix(flags, A0, Ai, Aj, Ak, fractions); + + if (phi) { + ApplyGhostFluidDiagonal(A0, flags, *phi, gfClamp); + } + + // check whether we need to fix some pressure value... + // (manually enable, or automatically for high accuracy, can cause asymmetries otherwise) + if (zeroPressureFixing || cgAccuracy < 1e-07) { + if (FLOATINGPOINT_PRECISION == 1) + debMsg( + "Warning - high CG accuracy with single-precision floating point accuracy might not " + "converge...", + 2); + + int numEmpty = CountEmptyCells(flags); + IndexInt fixPidx = -1; + if (numEmpty == 0) { + // Determine appropriate fluid cell for pressure fixing + // 1) First check some preferred positions for approx. symmetric zeroPressureFixing + Vec3i topCenter( + flags.getSizeX() / 2, flags.getSizeY() - 1, flags.is3D() ? flags.getSizeZ() / 2 : 0); + Vec3i preferredPos[] = {topCenter, topCenter - Vec3i(0, 1, 0), topCenter - Vec3i(0, 2, 0)}; + + for (Vec3i pos : preferredPos) { + if (flags.isFluid(pos)) { + fixPidx = flags.index(pos); + break; + } + } + + // 2) Then search whole domain + if (fixPidx == -1) { + FOR_IJK_BND(flags, 1) + { + if (flags.isFluid(i, j, k)) { + fixPidx = flags.index(i, j, k); + // break FOR_IJK_BND loop + i = flags.getSizeX() - 1; + j = flags.getSizeY() - 1; + k = __kmax; + } + } + } + // debMsg("No empty cells! Fixing pressure of cell "<<fixPidx<<" to zero",1); + } + if (fixPidx >= 0) { + fixPressure(fixPidx, Real(0), rhs, A0, Ai, Aj, Ak); + static bool msgOnce = false; + if (!msgOnce) { + debMsg("Pinning pressure of cell " << fixPidx << " to zero", 2); + msgOnce = true; + } + } + } + + // CG setup + // note: the last factor increases the max iterations for 2d, which right now can't use a + // preconditioner + GridCgInterface *gcg; + if (vel.is3D()) + gcg = new GridCg<ApplyMatrix>(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + else + gcg = new GridCg<ApplyMatrix2D>( + pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + + gcg->setAccuracy(cgAccuracy); + gcg->setUseL2Norm(useL2Norm); + + int maxIter = 0; + + Grid<Real> *pca0 = nullptr, *pca1 = nullptr, *pca2 = nullptr, *pca3 = nullptr; + GridMg *pmg = nullptr; + + // optional preconditioning + if (preconditioner == PcNone || preconditioner == PcMIC) { + maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4); + + pca0 = new Grid<Real>(parent); + pca1 = new Grid<Real>(parent); + pca2 = new Grid<Real>(parent); + pca3 = new Grid<Real>(parent); + + gcg->setICPreconditioner(preconditioner == PcMIC ? GridCgInterface::PC_mICP : + GridCgInterface::PC_None, + pca0, + pca1, + pca2, + pca3); + } + else if (preconditioner == PcMGDynamic || preconditioner == PcMGStatic) { + maxIter = 100; + + pmg = gMapMG[parent]; + if (!pmg) { + pmg = new GridMg(pressure.getSize()); + gMapMG[parent] = pmg; + } + + gcg->setMGPreconditioner(GridCgInterface::PC_MGP, pmg); + } + + // CG solve + for (int iter = 0; iter < maxIter; iter++) { + if (!gcg->iterate()) + iter = maxIter; + if (iter < maxIter) + debMsg("FluidSolver::solvePressure iteration " << iter + << ", residual: " << gcg->getResNorm(), + 9); + } + debMsg("FluidSolver::solvePressure done. Iterations:" << gcg->getIterations() + << ", residual:" << gcg->getResNorm(), + 2); + + // Cleanup + if (gcg) + delete gcg; + if (pca0) + delete pca0; + if (pca1) + delete pca1; + if (pca2) + delete pca2; + if (pca3) + delete pca3; + + // PcMGDynamic: always delete multigrid solver after use + // PcMGStatic: keep multigrid solver for next solve + if (pmg && preconditioner == PcMGDynamic) + releaseMG(parent); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "solvePressureSystem", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &rhs = *_args.getPtr<Grid<Real>>("rhs", 0, &_lock); + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + Grid<Real> &pressure = *_args.getPtr<Grid<Real>>("pressure", 2, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 3, &_lock); + Real cgAccuracy = _args.getOpt<Real>("cgAccuracy", 4, 1e-3, &_lock); + const Grid<Real> *phi = _args.getPtrOpt<Grid<Real>>("phi", 5, 0, &_lock); + const Grid<Real> *perCellCorr = _args.getPtrOpt<Grid<Real>>("perCellCorr", 6, 0, &_lock); + const MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 7, 0, &_lock); + Real gfClamp = _args.getOpt<Real>("gfClamp", 8, 1e-04, &_lock); + Real cgMaxIterFac = _args.getOpt<Real>("cgMaxIterFac", 9, 1.5, &_lock); + bool precondition = _args.getOpt<bool>("precondition", 10, true, &_lock); + int preconditioner = _args.getOpt<int>("preconditioner", 11, PcMIC, &_lock); + const bool enforceCompatibility = _args.getOpt<bool>( + "enforceCompatibility", 12, false, &_lock); + const bool useL2Norm = _args.getOpt<bool>("useL2Norm", 13, false, &_lock); + const bool zeroPressureFixing = _args.getOpt<bool>("zeroPressureFixing", 14, false, &_lock); + const Grid<Real> *curv = _args.getPtrOpt<Grid<Real>>("curv", 15, NULL, &_lock); + const Real surfTens = _args.getOpt<Real>("surfTens", 16, 0., &_lock); + _retval = getPyNone(); + solvePressureSystem(rhs, + vel, + pressure, + flags, + cgAccuracy, + phi, + perCellCorr, + fractions, + gfClamp, + cgMaxIterFac, + precondition, + preconditioner, + enforceCompatibility, + useL2Norm, + zeroPressureFixing, + curv, + surfTens); + _args.check(); + } + pbFinalizePlugin(parent, "solvePressureSystem", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("solvePressureSystem", e.what()); + return 0; + } +} +static const Pb::Register _RP_solvePressureSystem("", "solvePressureSystem", _W_2); +extern "C" { +void PbRegister_solvePressureSystem() +{ + KEEP_UNUSED(_RP_solvePressureSystem); +} +} + +//! Apply pressure gradient to make velocity field divergence free + +void correctVelocity(MACGrid &vel, + Grid<Real> &pressure, + const FlagGrid &flags, + Real cgAccuracy = 1e-3, + const Grid<Real> *phi = 0, + const Grid<Real> *perCellCorr = 0, + const MACGrid *fractions = 0, + Real gfClamp = 1e-04, + Real cgMaxIterFac = 1.5, + bool precondition = true, + int preconditioner = PcMIC, + bool enforceCompatibility = false, + bool useL2Norm = false, + bool zeroPressureFixing = false, + const Grid<Real> *curv = NULL, + const Real surfTens = 0.) +{ + knCorrectVelocity(flags, vel, pressure); + if (phi) { + knCorrectVelocityGhostFluid(vel, flags, pressure, *phi, gfClamp, curv, surfTens); + // improve behavior of clamping for large time steps: + knReplaceClampedGhostFluidVels(vel, flags, pressure, *phi, gfClamp); + } +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "correctVelocity", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + Grid<Real> &pressure = *_args.getPtr<Grid<Real>>("pressure", 1, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 2, &_lock); + Real cgAccuracy = _args.getOpt<Real>("cgAccuracy", 3, 1e-3, &_lock); + const Grid<Real> *phi = _args.getPtrOpt<Grid<Real>>("phi", 4, 0, &_lock); + const Grid<Real> *perCellCorr = _args.getPtrOpt<Grid<Real>>("perCellCorr", 5, 0, &_lock); + const MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 6, 0, &_lock); + Real gfClamp = _args.getOpt<Real>("gfClamp", 7, 1e-04, &_lock); + Real cgMaxIterFac = _args.getOpt<Real>("cgMaxIterFac", 8, 1.5, &_lock); + bool precondition = _args.getOpt<bool>("precondition", 9, true, &_lock); + int preconditioner = _args.getOpt<int>("preconditioner", 10, PcMIC, &_lock); + bool enforceCompatibility = _args.getOpt<bool>("enforceCompatibility", 11, false, &_lock); + bool useL2Norm = _args.getOpt<bool>("useL2Norm", 12, false, &_lock); + bool zeroPressureFixing = _args.getOpt<bool>("zeroPressureFixing", 13, false, &_lock); + const Grid<Real> *curv = _args.getPtrOpt<Grid<Real>>("curv", 14, NULL, &_lock); + const Real surfTens = _args.getOpt<Real>("surfTens", 15, 0., &_lock); + _retval = getPyNone(); + correctVelocity(vel, + pressure, + flags, + cgAccuracy, + phi, + perCellCorr, + fractions, + gfClamp, + cgMaxIterFac, + precondition, + preconditioner, + enforceCompatibility, + useL2Norm, + zeroPressureFixing, + curv, + surfTens); + _args.check(); + } + pbFinalizePlugin(parent, "correctVelocity", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("correctVelocity", e.what()); + return 0; + } +} +static const Pb::Register _RP_correctVelocity("", "correctVelocity", _W_3); +extern "C" { +void PbRegister_correctVelocity() +{ + KEEP_UNUSED(_RP_correctVelocity); +} +} + +//! Perform pressure projection of the velocity grid, calls +//! all three pressure helper functions in a row. + +void solvePressure(MACGrid &vel, + Grid<Real> &pressure, + const FlagGrid &flags, + Real cgAccuracy = 1e-3, + const Grid<Real> *phi = 0, + const Grid<Real> *perCellCorr = 0, + const MACGrid *fractions = 0, + const MACGrid *obvel = 0, + Real gfClamp = 1e-04, + Real cgMaxIterFac = 1.5, + bool precondition = true, + int preconditioner = PcMIC, + bool enforceCompatibility = false, + bool useL2Norm = false, + bool zeroPressureFixing = false, + const Grid<Real> *curv = NULL, + const Real surfTens = 0., + Grid<Real> *retRhs = NULL) +{ + Grid<Real> rhs(vel.getParent()); + + computePressureRhs(rhs, + vel, + pressure, + flags, + cgAccuracy, + phi, + perCellCorr, + fractions, + obvel, + gfClamp, + cgMaxIterFac, + precondition, + preconditioner, + enforceCompatibility, + useL2Norm, + zeroPressureFixing, + curv, + surfTens); + + solvePressureSystem(rhs, + vel, + pressure, + flags, + cgAccuracy, + phi, + perCellCorr, + fractions, + gfClamp, + cgMaxIterFac, + precondition, + preconditioner, + enforceCompatibility, + useL2Norm, + zeroPressureFixing, + curv, + surfTens); + + correctVelocity(vel, + pressure, + flags, + cgAccuracy, + phi, + perCellCorr, + fractions, + gfClamp, + cgMaxIterFac, + precondition, + preconditioner, + enforceCompatibility, + useL2Norm, + zeroPressureFixing, + curv, + surfTens); + + // optionally , return RHS + if (retRhs) { + retRhs->copyFrom(rhs); + } +} +static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "solvePressure", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + Grid<Real> &pressure = *_args.getPtr<Grid<Real>>("pressure", 1, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 2, &_lock); + Real cgAccuracy = _args.getOpt<Real>("cgAccuracy", 3, 1e-3, &_lock); + const Grid<Real> *phi = _args.getPtrOpt<Grid<Real>>("phi", 4, 0, &_lock); + const Grid<Real> *perCellCorr = _args.getPtrOpt<Grid<Real>>("perCellCorr", 5, 0, &_lock); + const MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 6, 0, &_lock); + const MACGrid *obvel = _args.getPtrOpt<MACGrid>("obvel", 7, 0, &_lock); + Real gfClamp = _args.getOpt<Real>("gfClamp", 8, 1e-04, &_lock); + Real cgMaxIterFac = _args.getOpt<Real>("cgMaxIterFac", 9, 1.5, &_lock); + bool precondition = _args.getOpt<bool>("precondition", 10, true, &_lock); + int preconditioner = _args.getOpt<int>("preconditioner", 11, PcMIC, &_lock); + bool enforceCompatibility = _args.getOpt<bool>("enforceCompatibility", 12, false, &_lock); + bool useL2Norm = _args.getOpt<bool>("useL2Norm", 13, false, &_lock); + bool zeroPressureFixing = _args.getOpt<bool>("zeroPressureFixing", 14, false, &_lock); + const Grid<Real> *curv = _args.getPtrOpt<Grid<Real>>("curv", 15, NULL, &_lock); + const Real surfTens = _args.getOpt<Real>("surfTens", 16, 0., &_lock); + Grid<Real> *retRhs = _args.getPtrOpt<Grid<Real>>("retRhs", 17, NULL, &_lock); + _retval = getPyNone(); + solvePressure(vel, + pressure, + flags, + cgAccuracy, + phi, + perCellCorr, + fractions, + obvel, + gfClamp, + cgMaxIterFac, + precondition, + preconditioner, + enforceCompatibility, + useL2Norm, + zeroPressureFixing, + curv, + surfTens, + retRhs); + _args.check(); + } + pbFinalizePlugin(parent, "solvePressure", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("solvePressure", e.what()); + return 0; + } +} +static const Pb::Register _RP_solvePressure("", "solvePressure", _W_4); +extern "C" { +void PbRegister_solvePressure() +{ + KEEP_UNUSED(_RP_solvePressure); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/ptsplugins.cpp b/extern/mantaflow/preprocessed/plugin/ptsplugins.cpp new file mode 100644 index 00000000000..a6bbccc5966 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/ptsplugins.cpp @@ -0,0 +1,502 @@ + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + +// ---------------------------------------------------------------------------- +// +// MantaFlow fluid solver framework +// Copyright 2018 Kiwon Um, Nils Thuerey +// +// This program is free software, distributed under the terms of the +// GNU General Public License (GPL) +// http://www.gnu.org/licenses +// +// Particle system helper +// +// ---------------------------------------------------------------------------- + +#include "particle.h" + +namespace Manta { + +struct KnAddForcePvel : public KernelBase { + KnAddForcePvel(ParticleDataImpl<Vec3> &v, + const Vec3 &da, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(v.size()), v(v), da(da), ptype(ptype), exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + ParticleDataImpl<Vec3> &v, + const Vec3 &da, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + if (ptype && ((*ptype)[idx] & exclude)) + return; + v[idx] += da; + } + inline ParticleDataImpl<Vec3> &getArg0() + { + return v; + } + typedef ParticleDataImpl<Vec3> type0; + inline const Vec3 &getArg1() + { + return da; + } + typedef Vec3 type1; + inline const ParticleDataImpl<int> *getArg2() + { + return ptype; + } + typedef ParticleDataImpl<int> type2; + inline const int &getArg3() + { + return exclude; + } + typedef int type3; + void runMessage() + { + debMsg("Executing kernel KnAddForcePvel ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, v, da, ptype, exclude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + ParticleDataImpl<Vec3> &v; + const Vec3 &da; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; +//! add force to vec3 particle data; a: acceleration + +void addForcePvel(ParticleDataImpl<Vec3> &vel, + const Vec3 &a, + const Real dt, + const ParticleDataImpl<int> *ptype, + const int exclude) +{ + KnAddForcePvel(vel, a * dt, ptype, exclude); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "addForcePvel", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + ParticleDataImpl<Vec3> &vel = *_args.getPtr<ParticleDataImpl<Vec3>>("vel", 0, &_lock); + const Vec3 &a = _args.get<Vec3>("a", 1, &_lock); + const Real dt = _args.get<Real>("dt", 2, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtr<ParticleDataImpl<int>>("ptype", 3, &_lock); + const int exclude = _args.get<int>("exclude", 4, &_lock); + _retval = getPyNone(); + addForcePvel(vel, a, dt, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "addForcePvel", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("addForcePvel", e.what()); + return 0; + } +} +static const Pb::Register _RP_addForcePvel("", "addForcePvel", _W_0); +extern "C" { +void PbRegister_addForcePvel() +{ + KEEP_UNUSED(_RP_addForcePvel); +} +} + +struct KnUpdateVelocityFromDeltaPos : public KernelBase { + KnUpdateVelocityFromDeltaPos(const BasicParticleSystem &p, + ParticleDataImpl<Vec3> &v, + const ParticleDataImpl<Vec3> &x_prev, + const Real over_dt, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(p.size()), + p(p), + v(v), + x_prev(x_prev), + over_dt(over_dt), + ptype(ptype), + exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystem &p, + ParticleDataImpl<Vec3> &v, + const ParticleDataImpl<Vec3> &x_prev, + const Real over_dt, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + if (ptype && ((*ptype)[idx] & exclude)) + return; + v[idx] = (p[idx].pos - x_prev[idx]) * over_dt; + } + inline const BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline ParticleDataImpl<Vec3> &getArg1() + { + return v; + } + typedef ParticleDataImpl<Vec3> type1; + inline const ParticleDataImpl<Vec3> &getArg2() + { + return x_prev; + } + typedef ParticleDataImpl<Vec3> type2; + inline const Real &getArg3() + { + return over_dt; + } + typedef Real type3; + inline const ParticleDataImpl<int> *getArg4() + { + return ptype; + } + typedef ParticleDataImpl<int> type4; + inline const int &getArg5() + { + return exclude; + } + typedef int type5; + void runMessage() + { + debMsg("Executing kernel KnUpdateVelocityFromDeltaPos ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, p, v, x_prev, over_dt, ptype, exclude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystem &p; + ParticleDataImpl<Vec3> &v; + const ParticleDataImpl<Vec3> &x_prev; + const Real over_dt; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; +//! retrieve velocity from position change + +void updateVelocityFromDeltaPos(const BasicParticleSystem &parts, + ParticleDataImpl<Vec3> &vel, + const ParticleDataImpl<Vec3> &x_prev, + const Real dt, + const ParticleDataImpl<int> *ptype, + const int exclude) +{ + KnUpdateVelocityFromDeltaPos(parts, vel, x_prev, 1.0 / dt, ptype, exclude); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "updateVelocityFromDeltaPos", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + ParticleDataImpl<Vec3> &vel = *_args.getPtr<ParticleDataImpl<Vec3>>("vel", 1, &_lock); + const ParticleDataImpl<Vec3> &x_prev = *_args.getPtr<ParticleDataImpl<Vec3>>( + "x_prev", 2, &_lock); + const Real dt = _args.get<Real>("dt", 3, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtr<ParticleDataImpl<int>>("ptype", 4, &_lock); + const int exclude = _args.get<int>("exclude", 5, &_lock); + _retval = getPyNone(); + updateVelocityFromDeltaPos(parts, vel, x_prev, dt, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "updateVelocityFromDeltaPos", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("updateVelocityFromDeltaPos", e.what()); + return 0; + } +} +static const Pb::Register _RP_updateVelocityFromDeltaPos("", "updateVelocityFromDeltaPos", _W_1); +extern "C" { +void PbRegister_updateVelocityFromDeltaPos() +{ + KEEP_UNUSED(_RP_updateVelocityFromDeltaPos); +} +} + +struct KnStepEuler : public KernelBase { + KnStepEuler(BasicParticleSystem &p, + const ParticleDataImpl<Vec3> &v, + const Real dt, + const ParticleDataImpl<int> *ptype, + const int exclude) + : KernelBase(p.size()), p(p), v(v), dt(dt), ptype(ptype), exclude(exclude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + BasicParticleSystem &p, + const ParticleDataImpl<Vec3> &v, + const Real dt, + const ParticleDataImpl<int> *ptype, + const int exclude) const + { + if (ptype && ((*ptype)[idx] & exclude)) + return; + p[idx].pos += v[idx] * dt; + } + inline BasicParticleSystem &getArg0() + { + return p; + } + typedef BasicParticleSystem type0; + inline const ParticleDataImpl<Vec3> &getArg1() + { + return v; + } + typedef ParticleDataImpl<Vec3> type1; + inline const Real &getArg2() + { + return dt; + } + typedef Real type2; + inline const ParticleDataImpl<int> *getArg3() + { + return ptype; + } + typedef ParticleDataImpl<int> type3; + inline const int &getArg4() + { + return exclude; + } + typedef int type4; + void runMessage() + { + debMsg("Executing kernel KnStepEuler ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, p, v, dt, ptype, exclude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystem &p; + const ParticleDataImpl<Vec3> &v; + const Real dt; + const ParticleDataImpl<int> *ptype; + const int exclude; +}; +//! simple foward Euler integration for particle system + +void eulerStep(BasicParticleSystem &parts, + const ParticleDataImpl<Vec3> &vel, + const ParticleDataImpl<int> *ptype, + const int exclude) +{ + KnStepEuler(parts, vel, parts.getParent()->getDt(), ptype, exclude); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "eulerStep", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + const ParticleDataImpl<Vec3> &vel = *_args.getPtr<ParticleDataImpl<Vec3>>("vel", 1, &_lock); + const ParticleDataImpl<int> *ptype = _args.getPtr<ParticleDataImpl<int>>("ptype", 2, &_lock); + const int exclude = _args.get<int>("exclude", 3, &_lock); + _retval = getPyNone(); + eulerStep(parts, vel, ptype, exclude); + _args.check(); + } + pbFinalizePlugin(parent, "eulerStep", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("eulerStep", e.what()); + return 0; + } +} +static const Pb::Register _RP_eulerStep("", "eulerStep", _W_2); +extern "C" { +void PbRegister_eulerStep() +{ + KEEP_UNUSED(_RP_eulerStep); +} +} + +struct KnSetPartType : public KernelBase { + KnSetPartType(ParticleDataImpl<int> &ptype, + const BasicParticleSystem &part, + const int mark, + const int stype, + const FlagGrid &flags, + const int cflag) + : KernelBase(ptype.size()), + ptype(ptype), + part(part), + mark(mark), + stype(stype), + flags(flags), + cflag(cflag) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + ParticleDataImpl<int> &ptype, + const BasicParticleSystem &part, + const int mark, + const int stype, + const FlagGrid &flags, + const int cflag) const + { + if (flags.isInBounds(part.getPos(idx), 0) && (flags.getAt(part.getPos(idx)) & cflag) && + (ptype[idx] & stype)) + ptype[idx] = mark; + } + inline ParticleDataImpl<int> &getArg0() + { + return ptype; + } + typedef ParticleDataImpl<int> type0; + inline const BasicParticleSystem &getArg1() + { + return part; + } + typedef BasicParticleSystem type1; + inline const int &getArg2() + { + return mark; + } + typedef int type2; + inline const int &getArg3() + { + return stype; + } + typedef int type3; + inline const FlagGrid &getArg4() + { + return flags; + } + typedef FlagGrid type4; + inline const int &getArg5() + { + return cflag; + } + typedef int type5; + void runMessage() + { + debMsg("Executing kernel KnSetPartType ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, ptype, part, mark, stype, flags, cflag); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + ParticleDataImpl<int> &ptype; + const BasicParticleSystem ∂ + const int mark; + const int stype; + const FlagGrid &flags; + const int cflag; +}; +//! if particle is stype and in cflag cell, set ptype as mark + +void setPartType(const BasicParticleSystem &parts, + ParticleDataImpl<int> &ptype, + const int mark, + const int stype, + const FlagGrid &flags, + const int cflag) +{ + KnSetPartType(ptype, parts, mark, stype, flags, cflag); +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setPartType", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + ParticleDataImpl<int> &ptype = *_args.getPtr<ParticleDataImpl<int>>("ptype", 1, &_lock); + const int mark = _args.get<int>("mark", 2, &_lock); + const int stype = _args.get<int>("stype", 3, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 4, &_lock); + const int cflag = _args.get<int>("cflag", 5, &_lock); + _retval = getPyNone(); + setPartType(parts, ptype, mark, stype, flags, cflag); + _args.check(); + } + pbFinalizePlugin(parent, "setPartType", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setPartType", e.what()); + return 0; + } +} +static const Pb::Register _RP_setPartType("", "setPartType", _W_3); +extern "C" { +void PbRegister_setPartType() +{ + KEEP_UNUSED(_RP_setPartType); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp new file mode 100644 index 00000000000..281e12ef04b --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp @@ -0,0 +1,3065 @@ + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2017 Georg Kohl, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * GNU General Public License (GPL) + * http://www.gnu.org/licenses + * + * Secondary particle plugin for FLIP simulations + * + ******************************************************************************/ + +#include "particle.h" +#include "commonkernels.h" + +namespace Manta { + +#pragma region Secondary Particles for FLIP +//---------------------------------------------------------------------------------------------------------------------------------------------------- +// Secondary Particles for FLIP +//---------------------------------------------------------------------------------------------------------------------------------------------------- + +// helper function that clamps the value in potential to the interval [tauMin, tauMax] and +// normalizes it to [0, 1] afterwards +Real clampPotential(Real potential, Real tauMin, Real tauMax) +{ + return (std::min(potential, tauMax) - std::min(potential, tauMin)) / (tauMax - tauMin); +} + +// computes all three potentials(trapped air, wave crest, kinetic energy) and the neighbor ratio +// for every fluid cell and stores it in the respective grid. Is less readable but significantly +// faster than using seperate potential computation + +struct knFlipComputeSecondaryParticlePotentials : public KernelBase { + knFlipComputeSecondaryParticlePotentials(Grid<Real> &potTA, + Grid<Real> &potWC, + Grid<Real> &potKE, + Grid<Real> &neighborRatio, + const FlagGrid &flags, + const MACGrid &v, + const Grid<Vec3> &normal, + const int radius, + const Real tauMinTA, + const Real tauMaxTA, + const Real tauMinWC, + const Real tauMaxWC, + const Real tauMinKE, + const Real tauMaxKE, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeObstacle | + FlagGrid::TypeOutflow | + FlagGrid::TypeInflow) + : KernelBase(&potTA, radius), + potTA(potTA), + potWC(potWC), + potKE(potKE), + neighborRatio(neighborRatio), + flags(flags), + v(v), + normal(normal), + radius(radius), + tauMinTA(tauMinTA), + tauMaxTA(tauMaxTA), + tauMinWC(tauMinWC), + tauMaxWC(tauMaxWC), + tauMinKE(tauMinKE), + tauMaxKE(tauMaxKE), + scaleFromManta(scaleFromManta), + itype(itype), + jtype(jtype) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<Real> &potTA, + Grid<Real> &potWC, + Grid<Real> &potKE, + Grid<Real> &neighborRatio, + const FlagGrid &flags, + const MACGrid &v, + const Grid<Vec3> &normal, + const int radius, + const Real tauMinTA, + const Real tauMaxTA, + const Real tauMinWC, + const Real tauMaxWC, + const Real tauMinKE, + const Real tauMaxKE, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeObstacle | FlagGrid::TypeOutflow | + FlagGrid::TypeInflow) const + { + + if (!(flags(i, j, k) & itype)) + return; + + // compute trapped air potential + wave crest potential + neighbor ratio at once + const Vec3 &xi = scaleFromManta * Vec3(i, j, k); // scale to unit cube + const Vec3 &vi = scaleFromManta * v.getCentered(i, j, k); + const Vec3 &ni = getNormalized(normal(i, j, k)); + Real vdiff = 0; // for trapped air + Real kappa = 0; // for wave crests + int countFluid = 0; // for neighbor ratio + int countMaxFluid = 0; // for neighbor ratio + + // iterate over neighboring cells within radius + for (IndexInt x = i - radius; x <= i + radius; x++) { + for (IndexInt y = j - radius; y <= j + radius; y++) { + for (IndexInt z = k - radius; z <= k + radius; z++) { + if ((x == i && y == j && z == k) || !flags.isInBounds(Vec3i(x, y, z)) || + (flags(x, y, z) & jtype)) + continue; + + if (flags(x, y, z) & itype) { + countFluid++; + countMaxFluid++; + } + else { + countMaxFluid++; + } + + const Vec3 &xj = scaleFromManta * Vec3(x, y, z); // scale to unit cube + const Vec3 &vj = scaleFromManta * v.getCentered(x, y, z); + const Vec3 &nj = getNormalized(normal(x, y, z)); + const Vec3 xij = xi - xj; + const Vec3 vij = vi - vj; + Real h = !potTA.is3D() ? + 1.414 * radius : + 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius for h, due + // to squared resp. cubic neighbor area + vdiff += norm(vij) * (1 - dot(getNormalized(vij), getNormalized(xij))) * + (1 - norm(xij) / h); + + if (dot(getNormalized(xij), ni) < 0) { // identifies wave crests + kappa += (1 - dot(ni, nj)) * (1 - norm(xij) / h); + } + } + } + } + + neighborRatio(i, j, k) = float(countFluid) / float(countMaxFluid); + + potTA(i, j, k) = clampPotential(vdiff, tauMinTA, tauMaxTA); + if (dot(getNormalized(vi), ni) >= 0.6) { // avoid to mark boarders of the scene as wave crest + potWC(i, j, k) = clampPotential(kappa, tauMinWC, tauMaxWC); + } + else { + potWC(i, j, k) = Real(0); + } + + // compute kinetic energy potential + Real ek = + Real(0.5) * 125 * + normSquare( + vi); // use arbitrary constant for mass, potential adjusts with thresholds anyways + potKE(i, j, k) = clampPotential(ek, tauMinKE, tauMaxKE); + } + inline Grid<Real> &getArg0() + { + return potTA; + } + typedef Grid<Real> type0; + inline Grid<Real> &getArg1() + { + return potWC; + } + typedef Grid<Real> type1; + inline Grid<Real> &getArg2() + { + return potKE; + } + typedef Grid<Real> type2; + inline Grid<Real> &getArg3() + { + return neighborRatio; + } + typedef Grid<Real> type3; + inline const FlagGrid &getArg4() + { + return flags; + } + typedef FlagGrid type4; + inline const MACGrid &getArg5() + { + return v; + } + typedef MACGrid type5; + inline const Grid<Vec3> &getArg6() + { + return normal; + } + typedef Grid<Vec3> type6; + inline const int &getArg7() + { + return radius; + } + typedef int type7; + inline const Real &getArg8() + { + return tauMinTA; + } + typedef Real type8; + inline const Real &getArg9() + { + return tauMaxTA; + } + typedef Real type9; + inline const Real &getArg10() + { + return tauMinWC; + } + typedef Real type10; + inline const Real &getArg11() + { + return tauMaxWC; + } + typedef Real type11; + inline const Real &getArg12() + { + return tauMinKE; + } + typedef Real type12; + inline const Real &getArg13() + { + return tauMaxKE; + } + typedef Real type13; + inline const Real &getArg14() + { + return scaleFromManta; + } + typedef Real type14; + inline const int &getArg15() + { + return itype; + } + typedef int type15; + inline const int &getArg16() + { + return jtype; + } + typedef int type16; + void runMessage() + { + debMsg("Executing kernel knFlipComputeSecondaryParticlePotentials ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = radius; j < _maxY; j++) + for (int i = radius; i < _maxX; i++) + op(i, + j, + k, + potTA, + potWC, + potKE, + neighborRatio, + flags, + v, + normal, + radius, + tauMinTA, + tauMaxTA, + tauMinWC, + tauMaxWC, + tauMinKE, + tauMaxKE, + scaleFromManta, + itype, + jtype); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = radius; i < _maxX; i++) + op(i, + j, + k, + potTA, + potWC, + potKE, + neighborRatio, + flags, + v, + normal, + radius, + tauMinTA, + tauMaxTA, + tauMinWC, + tauMaxWC, + tauMinKE, + tauMaxKE, + scaleFromManta, + itype, + jtype); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(radius, maxY), *this); + } + Grid<Real> &potTA; + Grid<Real> &potWC; + Grid<Real> &potKE; + Grid<Real> &neighborRatio; + const FlagGrid &flags; + const MACGrid &v; + const Grid<Vec3> &normal; + const int radius; + const Real tauMinTA; + const Real tauMaxTA; + const Real tauMinWC; + const Real tauMaxWC; + const Real tauMinKE; + const Real tauMaxKE; + const Real scaleFromManta; + const int itype; + const int jtype; +}; + +void flipComputeSecondaryParticlePotentials(Grid<Real> &potTA, + Grid<Real> &potWC, + Grid<Real> &potKE, + Grid<Real> &neighborRatio, + const FlagGrid &flags, + const MACGrid &v, + Grid<Vec3> &normal, + const Grid<Real> &phi, + const int radius, + const Real tauMinTA, + const Real tauMaxTA, + const Real tauMinWC, + const Real tauMaxWC, + const Real tauMinKE, + const Real tauMaxKE, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeObstacle | + FlagGrid::TypeOutflow | + FlagGrid::TypeInflow) +{ + potTA.clear(); + potWC.clear(); + potKE.clear(); + neighborRatio.clear(); + GradientOp(normal, phi); + knFlipComputeSecondaryParticlePotentials(potTA, + potWC, + potKE, + neighborRatio, + flags, + v, + normal, + radius, + tauMinTA, + tauMaxTA, + tauMinWC, + tauMaxWC, + tauMinKE, + tauMaxKE, + scaleFromManta, + itype, + jtype); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipComputeSecondaryParticlePotentials", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &potTA = *_args.getPtr<Grid<Real>>("potTA", 0, &_lock); + Grid<Real> &potWC = *_args.getPtr<Grid<Real>>("potWC", 1, &_lock); + Grid<Real> &potKE = *_args.getPtr<Grid<Real>>("potKE", 2, &_lock); + Grid<Real> &neighborRatio = *_args.getPtr<Grid<Real>>("neighborRatio", 3, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 4, &_lock); + const MACGrid &v = *_args.getPtr<MACGrid>("v", 5, &_lock); + Grid<Vec3> &normal = *_args.getPtr<Grid<Vec3>>("normal", 6, &_lock); + const Grid<Real> &phi = *_args.getPtr<Grid<Real>>("phi", 7, &_lock); + const int radius = _args.get<int>("radius", 8, &_lock); + const Real tauMinTA = _args.get<Real>("tauMinTA", 9, &_lock); + const Real tauMaxTA = _args.get<Real>("tauMaxTA", 10, &_lock); + const Real tauMinWC = _args.get<Real>("tauMinWC", 11, &_lock); + const Real tauMaxWC = _args.get<Real>("tauMaxWC", 12, &_lock); + const Real tauMinKE = _args.get<Real>("tauMinKE", 13, &_lock); + const Real tauMaxKE = _args.get<Real>("tauMaxKE", 14, &_lock); + const Real scaleFromManta = _args.get<Real>("scaleFromManta", 15, &_lock); + const int itype = _args.getOpt<int>("itype", 16, FlagGrid::TypeFluid, &_lock); + const int jtype = _args.getOpt<int>("jtype", + 17, + FlagGrid::TypeObstacle | FlagGrid::TypeOutflow | + FlagGrid::TypeInflow, + &_lock); + _retval = getPyNone(); + flipComputeSecondaryParticlePotentials(potTA, + potWC, + potKE, + neighborRatio, + flags, + v, + normal, + phi, + radius, + tauMinTA, + tauMaxTA, + tauMinWC, + tauMaxWC, + tauMinKE, + tauMaxKE, + scaleFromManta, + itype, + jtype); + _args.check(); + } + pbFinalizePlugin(parent, "flipComputeSecondaryParticlePotentials", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipComputeSecondaryParticlePotentials", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipComputeSecondaryParticlePotentials( + "", "flipComputeSecondaryParticlePotentials", _W_0); +extern "C" { +void PbRegister_flipComputeSecondaryParticlePotentials() +{ + KEEP_UNUSED(_RP_flipComputeSecondaryParticlePotentials); +} +} + +// adds secondary particles to &pts_sec for every fluid cell in &flags according to the potential +// grids &potTA, &potWC and &potKE secondary particles are uniformly sampled in every fluid cell in +// a randomly offset cylinder in fluid movement direction In contrast to +// flipSampleSecondaryParticles this uses more cylinders per cell and interpolates velocity and +// potentials. To control number of cylinders in each dimension adjust radius(0.25=>2 cyl, +// 0.1666=>3 cyl, 0.125=>3cyl etc.). + +struct knFlipSampleSecondaryParticlesMoreCylinders : public KernelBase { + knFlipSampleSecondaryParticlesMoreCylinders(const FlagGrid &flags, + const MACGrid &v, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const Real lMin, + const Real lMax, + const Grid<Real> &potTA, + const Grid<Real> &potWC, + const Grid<Real> &potKE, + const Grid<Real> &neighborRatio, + const Real c_s, + const Real c_b, + const Real k_ta, + const Real k_wc, + const Real dt, + const int itype = FlagGrid::TypeFluid) + : KernelBase(&flags, 0), + flags(flags), + v(v), + pts_sec(pts_sec), + v_sec(v_sec), + l_sec(l_sec), + lMin(lMin), + lMax(lMax), + potTA(potTA), + potWC(potWC), + potKE(potKE), + neighborRatio(neighborRatio), + c_s(c_s), + c_b(c_b), + k_ta(k_ta), + k_wc(k_wc), + dt(dt), + itype(itype) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &v, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const Real lMin, + const Real lMax, + const Grid<Real> &potTA, + const Grid<Real> &potWC, + const Grid<Real> &potKE, + const Grid<Real> &neighborRatio, + const Real c_s, + const Real c_b, + const Real k_ta, + const Real k_wc, + const Real dt, + const int itype = FlagGrid::TypeFluid) + { + + if (!(flags(i, j, k) & itype)) + return; + + RandomStream mRand(9832); + Real radius = + 0.25; // diameter=0.5 => sampling with two cylinders in each dimension since cell size=1 + for (Real x = i - radius; x <= i + radius; x += 2 * radius) { + for (Real y = j - radius; y <= j + radius; y += 2 * radius) { + for (Real z = k - radius; z <= k + radius; z += 2 * radius) { + + Vec3 xi = Vec3(x, y, z); + Real KE = potKE.getInterpolated(xi); + Real TA = potTA.getInterpolated(xi); + Real WC = potWC.getInterpolated(xi); + + const int n = KE * (k_ta * TA + k_wc * WC) * dt; // number of secondary particles + if (n == 0) + continue; + Vec3 vi = v.getInterpolated(xi); + Vec3 dir = dt * vi; // direction of movement of current particle + Vec3 e1 = getNormalized(Vec3(dir.z, 0, -dir.x)); // perpendicular to dir + Vec3 e2 = getNormalized( + cross(e1, dir)); // perpendicular to dir and e1, so e1 and e1 create reference plane + + for (int di = 0; di < n; di++) { + const Real r = radius * sqrt(mRand.getReal()); // distance to cylinder axis + const Real theta = mRand.getReal() * Real(2) * M_PI; // azimuth + const Real h = mRand.getReal() * norm(dt * vi); // distance to reference plane + Vec3 xd = xi + r * cos(theta) * e1 + r * sin(theta) * e2 + h * getNormalized(vi); + if (!flags.is3D()) + xd.z = 0; + pts_sec.add(xd); + + v_sec[v_sec.size() - 1] = r * cos(theta) * e1 + r * sin(theta) * e2 + + vi; // init velocity of new particle + Real temp = (KE + TA + WC) / 3; + l_sec[l_sec.size() - 1] = ((lMax - lMin) * temp) + lMin + + mRand.getReal() * 0.1; // init lifetime of new particle + + // init type of new particle + if (neighborRatio(i, j, k) < c_s) { + pts_sec[pts_sec.size() - 1].flag = ParticleBase::PSPRAY; + } + else if (neighborRatio(i, j, k) > c_b) { + pts_sec[pts_sec.size() - 1].flag = ParticleBase::PBUBBLE; + } + else { + pts_sec[pts_sec.size() - 1].flag = ParticleBase::PFOAM; + } + } + } + } + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return v; + } + typedef MACGrid type1; + inline BasicParticleSystem &getArg2() + { + return pts_sec; + } + typedef BasicParticleSystem type2; + inline ParticleDataImpl<Vec3> &getArg3() + { + return v_sec; + } + typedef ParticleDataImpl<Vec3> type3; + inline ParticleDataImpl<Real> &getArg4() + { + return l_sec; + } + typedef ParticleDataImpl<Real> type4; + inline const Real &getArg5() + { + return lMin; + } + typedef Real type5; + inline const Real &getArg6() + { + return lMax; + } + typedef Real type6; + inline const Grid<Real> &getArg7() + { + return potTA; + } + typedef Grid<Real> type7; + inline const Grid<Real> &getArg8() + { + return potWC; + } + typedef Grid<Real> type8; + inline const Grid<Real> &getArg9() + { + return potKE; + } + typedef Grid<Real> type9; + inline const Grid<Real> &getArg10() + { + return neighborRatio; + } + typedef Grid<Real> type10; + inline const Real &getArg11() + { + return c_s; + } + typedef Real type11; + inline const Real &getArg12() + { + return c_b; + } + typedef Real type12; + inline const Real &getArg13() + { + return k_ta; + } + typedef Real type13; + inline const Real &getArg14() + { + return k_wc; + } + typedef Real type14; + inline const Real &getArg15() + { + return dt; + } + typedef Real type15; + inline const int &getArg16() + { + return itype; + } + typedef int type16; + void runMessage() + { + debMsg("Executing kernel knFlipSampleSecondaryParticlesMoreCylinders ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void run() + { + const int _maxX = maxX; + const int _maxY = maxY; + for (int k = minZ; k < maxZ; k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, + j, + k, + flags, + v, + pts_sec, + v_sec, + l_sec, + lMin, + lMax, + potTA, + potWC, + potKE, + neighborRatio, + c_s, + c_b, + k_ta, + k_wc, + dt, + itype); + } + const FlagGrid &flags; + const MACGrid &v; + BasicParticleSystem &pts_sec; + ParticleDataImpl<Vec3> &v_sec; + ParticleDataImpl<Real> &l_sec; + const Real lMin; + const Real lMax; + const Grid<Real> &potTA; + const Grid<Real> &potWC; + const Grid<Real> &potKE; + const Grid<Real> &neighborRatio; + const Real c_s; + const Real c_b; + const Real k_ta; + const Real k_wc; + const Real dt; + const int itype; +}; + +// adds secondary particles to &pts_sec for every fluid cell in &flags according to the potential +// grids &potTA, &potWC and &potKE secondary particles are uniformly sampled in every fluid cell in +// a randomly offset cylinder in fluid movement direction + +struct knFlipSampleSecondaryParticles : public KernelBase { + knFlipSampleSecondaryParticles(const FlagGrid &flags, + const MACGrid &v, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const Real lMin, + const Real lMax, + const Grid<Real> &potTA, + const Grid<Real> &potWC, + const Grid<Real> &potKE, + const Grid<Real> &neighborRatio, + const Real c_s, + const Real c_b, + const Real k_ta, + const Real k_wc, + const Real dt, + const int itype = FlagGrid::TypeFluid) + : KernelBase(&flags, 0), + flags(flags), + v(v), + pts_sec(pts_sec), + v_sec(v_sec), + l_sec(l_sec), + lMin(lMin), + lMax(lMax), + potTA(potTA), + potWC(potWC), + potKE(potKE), + neighborRatio(neighborRatio), + c_s(c_s), + c_b(c_b), + k_ta(k_ta), + k_wc(k_wc), + dt(dt), + itype(itype) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + const MACGrid &v, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const Real lMin, + const Real lMax, + const Grid<Real> &potTA, + const Grid<Real> &potWC, + const Grid<Real> &potKE, + const Grid<Real> &neighborRatio, + const Real c_s, + const Real c_b, + const Real k_ta, + const Real k_wc, + const Real dt, + const int itype = FlagGrid::TypeFluid) + { + + if (!(flags(i, j, k) & itype)) + return; + + Real KE = potKE(i, j, k); + Real TA = potTA(i, j, k); + Real WC = potWC(i, j, k); + + const int n = KE * (k_ta * TA + k_wc * WC) * dt; // number of secondary particles + if (n == 0) + return; + RandomStream mRand(9832); + + Vec3 xi = Vec3(i + mRand.getReal(), + j + mRand.getReal(), + k + mRand.getReal()); // randomized offset uniform in cell + Vec3 vi = v.getInterpolated(xi); + Vec3 dir = dt * vi; // direction of movement of current particle + Vec3 e1 = getNormalized(Vec3(dir.z, 0, -dir.x)); // perpendicular to dir + Vec3 e2 = getNormalized( + cross(e1, dir)); // perpendicular to dir and e1, so e1 and e1 create reference plane + + for (int di = 0; di < n; di++) { + const Real r = Real(0.5) * sqrt(mRand.getReal()); // distance to cylinder axis + const Real theta = mRand.getReal() * Real(2) * M_PI; // azimuth + const Real h = mRand.getReal() * norm(dt * vi); // distance to reference plane + Vec3 xd = xi + r * cos(theta) * e1 + r * sin(theta) * e2 + h * getNormalized(vi); + if (!flags.is3D()) + xd.z = 0; + pts_sec.add(xd); + + v_sec[v_sec.size() - 1] = r * cos(theta) * e1 + r * sin(theta) * e2 + + vi; // init velocity of new particle + Real temp = (KE + TA + WC) / 3; + l_sec[l_sec.size() - 1] = ((lMax - lMin) * temp) + lMin + + mRand.getReal() * 0.1; // init lifetime of new particle + + // init type of new particle + if (neighborRatio(i, j, k) < c_s) { + pts_sec[pts_sec.size() - 1].flag = ParticleBase::PSPRAY; + } + else if (neighborRatio(i, j, k) > c_b) { + pts_sec[pts_sec.size() - 1].flag = ParticleBase::PBUBBLE; + } + else { + pts_sec[pts_sec.size() - 1].flag = ParticleBase::PFOAM; + } + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return v; + } + typedef MACGrid type1; + inline BasicParticleSystem &getArg2() + { + return pts_sec; + } + typedef BasicParticleSystem type2; + inline ParticleDataImpl<Vec3> &getArg3() + { + return v_sec; + } + typedef ParticleDataImpl<Vec3> type3; + inline ParticleDataImpl<Real> &getArg4() + { + return l_sec; + } + typedef ParticleDataImpl<Real> type4; + inline const Real &getArg5() + { + return lMin; + } + typedef Real type5; + inline const Real &getArg6() + { + return lMax; + } + typedef Real type6; + inline const Grid<Real> &getArg7() + { + return potTA; + } + typedef Grid<Real> type7; + inline const Grid<Real> &getArg8() + { + return potWC; + } + typedef Grid<Real> type8; + inline const Grid<Real> &getArg9() + { + return potKE; + } + typedef Grid<Real> type9; + inline const Grid<Real> &getArg10() + { + return neighborRatio; + } + typedef Grid<Real> type10; + inline const Real &getArg11() + { + return c_s; + } + typedef Real type11; + inline const Real &getArg12() + { + return c_b; + } + typedef Real type12; + inline const Real &getArg13() + { + return k_ta; + } + typedef Real type13; + inline const Real &getArg14() + { + return k_wc; + } + typedef Real type14; + inline const Real &getArg15() + { + return dt; + } + typedef Real type15; + inline const int &getArg16() + { + return itype; + } + typedef int type16; + void runMessage() + { + debMsg("Executing kernel knFlipSampleSecondaryParticles ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void run() + { + const int _maxX = maxX; + const int _maxY = maxY; + for (int k = minZ; k < maxZ; k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, + j, + k, + flags, + v, + pts_sec, + v_sec, + l_sec, + lMin, + lMax, + potTA, + potWC, + potKE, + neighborRatio, + c_s, + c_b, + k_ta, + k_wc, + dt, + itype); + } + const FlagGrid &flags; + const MACGrid &v; + BasicParticleSystem &pts_sec; + ParticleDataImpl<Vec3> &v_sec; + ParticleDataImpl<Real> &l_sec; + const Real lMin; + const Real lMax; + const Grid<Real> &potTA; + const Grid<Real> &potWC; + const Grid<Real> &potKE; + const Grid<Real> &neighborRatio; + const Real c_s; + const Real c_b; + const Real k_ta; + const Real k_wc; + const Real dt; + const int itype; +}; + +void flipSampleSecondaryParticles(const std::string mode, + const FlagGrid &flags, + const MACGrid &v, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const Real lMin, + const Real lMax, + const Grid<Real> &potTA, + const Grid<Real> &potWC, + const Grid<Real> &potKE, + const Grid<Real> &neighborRatio, + const Real c_s, + const Real c_b, + const Real k_ta, + const Real k_wc, + const Real dt, + const int itype = FlagGrid::TypeFluid) +{ + if (mode == "single") { + knFlipSampleSecondaryParticles(flags, + v, + pts_sec, + v_sec, + l_sec, + lMin, + lMax, + potTA, + potWC, + potKE, + neighborRatio, + c_s, + c_b, + k_ta, + k_wc, + dt, + itype); + } + else if (mode == "multiple") { + knFlipSampleSecondaryParticlesMoreCylinders(flags, + v, + pts_sec, + v_sec, + l_sec, + lMin, + lMax, + potTA, + potWC, + potKE, + neighborRatio, + c_s, + c_b, + k_ta, + k_wc, + dt, + itype); + } + else { + throw std::invalid_argument("Unknown mode: use \"single\" or \"multiple\" instead!"); + } +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipSampleSecondaryParticles", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const std::string mode = _args.get<std::string>("mode", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + const MACGrid &v = *_args.getPtr<MACGrid>("v", 2, &_lock); + BasicParticleSystem &pts_sec = *_args.getPtr<BasicParticleSystem>("pts_sec", 3, &_lock); + ParticleDataImpl<Vec3> &v_sec = *_args.getPtr<ParticleDataImpl<Vec3>>("v_sec", 4, &_lock); + ParticleDataImpl<Real> &l_sec = *_args.getPtr<ParticleDataImpl<Real>>("l_sec", 5, &_lock); + const Real lMin = _args.get<Real>("lMin", 6, &_lock); + const Real lMax = _args.get<Real>("lMax", 7, &_lock); + const Grid<Real> &potTA = *_args.getPtr<Grid<Real>>("potTA", 8, &_lock); + const Grid<Real> &potWC = *_args.getPtr<Grid<Real>>("potWC", 9, &_lock); + const Grid<Real> &potKE = *_args.getPtr<Grid<Real>>("potKE", 10, &_lock); + const Grid<Real> &neighborRatio = *_args.getPtr<Grid<Real>>("neighborRatio", 11, &_lock); + const Real c_s = _args.get<Real>("c_s", 12, &_lock); + const Real c_b = _args.get<Real>("c_b", 13, &_lock); + const Real k_ta = _args.get<Real>("k_ta", 14, &_lock); + const Real k_wc = _args.get<Real>("k_wc", 15, &_lock); + const Real dt = _args.get<Real>("dt", 16, &_lock); + const int itype = _args.getOpt<int>("itype", 17, FlagGrid::TypeFluid, &_lock); + _retval = getPyNone(); + flipSampleSecondaryParticles(mode, + flags, + v, + pts_sec, + v_sec, + l_sec, + lMin, + lMax, + potTA, + potWC, + potKE, + neighborRatio, + c_s, + c_b, + k_ta, + k_wc, + dt, + itype); + _args.check(); + } + pbFinalizePlugin(parent, "flipSampleSecondaryParticles", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipSampleSecondaryParticles", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipSampleSecondaryParticles("", + "flipSampleSecondaryParticles", + _W_1); +extern "C" { +void PbRegister_flipSampleSecondaryParticles() +{ + KEEP_UNUSED(_RP_flipSampleSecondaryParticles); +} +} + +// evaluates cubic spline with radius h and distance l in dim dimensions +Real cubicSpline(const Real h, const Real l, const int dim) +{ + const Real h2 = square(h), h3 = h2 * h, h4 = h3 * h, h5 = h4 * h; + const Real c[] = { + Real(2e0 / (3e0 * h)), Real(10e0 / (7e0 * M_PI * h2)), Real(1e0 / (M_PI * h3))}; + const Real q = l / h; + if (q < 1e0) + return c[dim - 1] * (1e0 - 1.5 * square(q) + 0.75 * cubed(q)); + else if (q < 2e0) + return c[dim - 1] * (0.25 * cubed(2e0 - q)); + return 0; +} + +// updates position &pts_sec.pos and velocity &v_sec of secondary particles according to the +// particle type determined by the neighbor ratio with linear interpolation + +struct knFlipUpdateSecondaryParticlesLinear : public KernelBase { + knFlipUpdateSecondaryParticlesLinear(BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const ParticleDataImpl<Vec3> &f_sec, + const FlagGrid &flags, + const MACGrid &v, + const Grid<Real> &neighborRatio, + const Vec3 gravity, + const Real k_b, + const Real k_d, + const Real c_s, + const Real c_b, + const Real dt, + const int exclude, + const int antitunneling) + : KernelBase(pts_sec.size()), + pts_sec(pts_sec), + v_sec(v_sec), + l_sec(l_sec), + f_sec(f_sec), + flags(flags), + v(v), + neighborRatio(neighborRatio), + gravity(gravity), + k_b(k_b), + k_d(k_d), + c_s(c_s), + c_b(c_b), + dt(dt), + exclude(exclude), + antitunneling(antitunneling) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const ParticleDataImpl<Vec3> &f_sec, + const FlagGrid &flags, + const MACGrid &v, + const Grid<Real> &neighborRatio, + const Vec3 gravity, + const Real k_b, + const Real k_d, + const Real c_s, + const Real c_b, + const Real dt, + const int exclude, + const int antitunneling) const + { + + if (!pts_sec.isActive(idx) || pts_sec[idx].flag & exclude) + return; + if (!flags.isInBounds(pts_sec[idx].pos)) { + pts_sec.kill(idx); + return; + } + + Vec3i gridpos = toVec3i(pts_sec[idx].pos); + int i = gridpos.x; + int j = gridpos.y; + int k = gridpos.z; + + // spray particle + if (neighborRatio(gridpos) < c_s) { + pts_sec[idx].flag |= ParticleBase::PSPRAY; + pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PFOAM); + v_sec[idx] += dt * + ((f_sec[idx] / 1) + gravity); // TODO: if forces are added (e.g. fluid + // guiding), add parameter for mass instead of 1 + + // anti tunneling for small obstacles + for (int ct = 1; ct < antitunneling; ct++) { + Vec3i tempPos = toVec3i(pts_sec[idx].pos + + ct * (1 / Real(antitunneling)) * dt * v_sec[idx]); + if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) { + pts_sec.kill(idx); + return; + } + } + pts_sec[idx].pos += dt * v_sec[idx]; + } + + // air bubble particle + else if (neighborRatio(gridpos) > c_b) { + pts_sec[idx].flag |= ParticleBase::PBUBBLE; + pts_sec[idx].flag &= ~(ParticleBase::PSPRAY | ParticleBase::PFOAM); + + const Vec3 vj = (v.getInterpolated(pts_sec[idx].pos) - v_sec[idx]) / dt; + v_sec[idx] += dt * (k_b * -gravity + k_d * vj); + + // anti tunneling for small obstacles + for (int ct = 1; ct < antitunneling; ct++) { + Vec3i tempPos = toVec3i(pts_sec[idx].pos + + ct * (1 / Real(antitunneling)) * dt * v_sec[idx]); + if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) { + pts_sec.kill(idx); + return; + } + } + pts_sec[idx].pos += dt * v_sec[idx]; + } + + // foam particle + else { + pts_sec[idx].flag |= ParticleBase::PFOAM; + pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PSPRAY); + + const Vec3 vj = v.getInterpolated(pts_sec[idx].pos); + // anti tunneling for small obstacles + for (int ct = 1; ct < antitunneling; ct++) { + Vec3i tempPos = toVec3i(pts_sec[idx].pos + ct * (1 / Real(antitunneling)) * dt * vj); + if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) { + pts_sec.kill(idx); + return; + } + } + pts_sec[idx].pos += dt * v.getInterpolated(pts_sec[idx].pos); + } + + // lifetime + l_sec[idx] -= dt; + if (l_sec[idx] <= Real(0)) { + pts_sec.kill(idx); + } + } + inline BasicParticleSystem &getArg0() + { + return pts_sec; + } + typedef BasicParticleSystem type0; + inline ParticleDataImpl<Vec3> &getArg1() + { + return v_sec; + } + typedef ParticleDataImpl<Vec3> type1; + inline ParticleDataImpl<Real> &getArg2() + { + return l_sec; + } + typedef ParticleDataImpl<Real> type2; + inline const ParticleDataImpl<Vec3> &getArg3() + { + return f_sec; + } + typedef ParticleDataImpl<Vec3> type3; + inline const FlagGrid &getArg4() + { + return flags; + } + typedef FlagGrid type4; + inline const MACGrid &getArg5() + { + return v; + } + typedef MACGrid type5; + inline const Grid<Real> &getArg6() + { + return neighborRatio; + } + typedef Grid<Real> type6; + inline const Vec3 &getArg7() + { + return gravity; + } + typedef Vec3 type7; + inline const Real &getArg8() + { + return k_b; + } + typedef Real type8; + inline const Real &getArg9() + { + return k_d; + } + typedef Real type9; + inline const Real &getArg10() + { + return c_s; + } + typedef Real type10; + inline const Real &getArg11() + { + return c_b; + } + typedef Real type11; + inline const Real &getArg12() + { + return dt; + } + typedef Real type12; + inline const int &getArg13() + { + return exclude; + } + typedef int type13; + inline const int &getArg14() + { + return antitunneling; + } + typedef int type14; + void runMessage() + { + debMsg("Executing kernel knFlipUpdateSecondaryParticlesLinear ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, + pts_sec, + v_sec, + l_sec, + f_sec, + flags, + v, + neighborRatio, + gravity, + k_b, + k_d, + c_s, + c_b, + dt, + exclude, + antitunneling); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystem &pts_sec; + ParticleDataImpl<Vec3> &v_sec; + ParticleDataImpl<Real> &l_sec; + const ParticleDataImpl<Vec3> &f_sec; + const FlagGrid &flags; + const MACGrid &v; + const Grid<Real> &neighborRatio; + const Vec3 gravity; + const Real k_b; + const Real k_d; + const Real c_s; + const Real c_b; + const Real dt; + const int exclude; + const int antitunneling; +}; +// updates position &pts_sec.pos and velocity &v_sec of secondary particles according to the +// particle type determined by the neighbor ratio with cubic spline interpolation + +struct knFlipUpdateSecondaryParticlesCubic : public KernelBase { + knFlipUpdateSecondaryParticlesCubic(BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const ParticleDataImpl<Vec3> &f_sec, + const FlagGrid &flags, + const MACGrid &v, + const Grid<Real> &neighborRatio, + const int radius, + const Vec3 gravity, + const Real k_b, + const Real k_d, + const Real c_s, + const Real c_b, + const Real dt, + const int exclude, + const int antitunneling, + const int itype) + : KernelBase(pts_sec.size()), + pts_sec(pts_sec), + v_sec(v_sec), + l_sec(l_sec), + f_sec(f_sec), + flags(flags), + v(v), + neighborRatio(neighborRatio), + radius(radius), + gravity(gravity), + k_b(k_b), + k_d(k_d), + c_s(c_s), + c_b(c_b), + dt(dt), + exclude(exclude), + antitunneling(antitunneling), + itype(itype) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const ParticleDataImpl<Vec3> &f_sec, + const FlagGrid &flags, + const MACGrid &v, + const Grid<Real> &neighborRatio, + const int radius, + const Vec3 gravity, + const Real k_b, + const Real k_d, + const Real c_s, + const Real c_b, + const Real dt, + const int exclude, + const int antitunneling, + const int itype) const + { + + if (!pts_sec.isActive(idx) || pts_sec[idx].flag & exclude) + return; + if (!flags.isInBounds(pts_sec[idx].pos)) { + pts_sec.kill(idx); + return; + } + + Vec3i gridpos = toVec3i(pts_sec[idx].pos); + int i = gridpos.x; + int j = gridpos.y; + int k = gridpos.z; + + // spray particle + if (neighborRatio(gridpos) < c_s) { + pts_sec[idx].flag |= ParticleBase::PSPRAY; + pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PFOAM); + v_sec[idx] += dt * + ((f_sec[idx] / 1) + gravity); // TODO: if forces are added (e.g. fluid + // guiding), add parameter for mass instead of 1 + + // anti tunneling for small obstacles + for (int ct = 1; ct < antitunneling; ct++) { + Vec3i tempPos = toVec3i(pts_sec[idx].pos + + ct * (1 / Real(antitunneling)) * dt * v_sec[idx]); + if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) { + pts_sec.kill(idx); + return; + } + } + pts_sec[idx].pos += dt * v_sec[idx]; + } + + // air bubble particle + else if (neighborRatio(gridpos) > c_b) { + pts_sec[idx].flag |= ParticleBase::PBUBBLE; + pts_sec[idx].flag &= ~(ParticleBase::PSPRAY | ParticleBase::PFOAM); + const Vec3 &xi = pts_sec[idx].pos; + Vec3 sumNumerator = Vec3(0, 0, 0); + Real sumDenominator = 0; + for (IndexInt x = i - radius; x <= i + radius; x++) { + for (IndexInt y = j - radius; y <= j + radius; y++) { + for (IndexInt z = k - radius; z <= k + radius; z++) { + Vec3i xj = Vec3i(x, y, z); + if ((x == i && y == j && z == k) || !flags.isInBounds(xj)) + continue; + if (!(flags(xj) & itype)) + continue; + const Real len_xij = norm(xi - Vec3(x, y, z)); + + int dim = flags.is3D() ? 3 : 2; + Real dist = flags.is3D() ? 1.732 : 1.414; + Real weight = cubicSpline(radius * dist, len_xij, dim); + sumNumerator += v.getCentered(xj) * + weight; // estimate next position by current velocity + sumDenominator += weight; + } + } + } + const Vec3 temp = ((sumNumerator / sumDenominator) - v_sec[idx]) / dt; + v_sec[idx] += dt * (k_b * -gravity + k_d * temp); + + // anti tunneling for small obstacles + for (int ct = 1; ct < antitunneling; ct++) { + Vec3i tempPos = toVec3i(pts_sec[idx].pos + + ct * (1 / Real(antitunneling)) * dt * v_sec[idx]); + if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) { + pts_sec.kill(idx); + return; + } + } + pts_sec[idx].pos += dt * v_sec[idx]; + } + + // foam particle + else { + pts_sec[idx].flag |= ParticleBase::PFOAM; + pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PSPRAY); + const Vec3 &xi = pts_sec[idx].pos; + Vec3 sumNumerator = Vec3(0, 0, 0); + Real sumDenominator = 0; + for (IndexInt x = i - radius; x <= i + radius; x++) { + for (IndexInt y = j - radius; y <= j + radius; y++) { + for (IndexInt z = k - radius; z <= k + radius; z++) { + Vec3i xj = Vec3i(x, y, z); + if ((x == i && y == j && z == k) || !flags.isInBounds(xj)) + continue; + if (!(flags(xj) & itype)) + continue; + const Real len_xij = norm(xi - Vec3(x, y, z)); + + int dim = flags.is3D() ? 3 : 2; + Real dist = flags.is3D() ? 1.732 : 1.414; + Real weight = cubicSpline(radius * dist, len_xij, dim); + sumNumerator += v.getCentered(xj) * + weight; // estimate next position by current velocity + sumDenominator += weight; + } + } + } + + // anti tunneling for small obstacles + for (int ct = 1; ct < antitunneling; ct++) { + Vec3i tempPos = toVec3i(pts_sec[idx].pos + ct * (1 / Real(antitunneling)) * dt * + (sumNumerator / sumDenominator)); + if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) { + pts_sec.kill(idx); + return; + } + } + pts_sec[idx].pos += dt * (sumNumerator / sumDenominator); + } + + // lifetime + l_sec[idx] -= dt; + if (l_sec[idx] <= Real(0)) { + pts_sec.kill(idx); + } + } + inline BasicParticleSystem &getArg0() + { + return pts_sec; + } + typedef BasicParticleSystem type0; + inline ParticleDataImpl<Vec3> &getArg1() + { + return v_sec; + } + typedef ParticleDataImpl<Vec3> type1; + inline ParticleDataImpl<Real> &getArg2() + { + return l_sec; + } + typedef ParticleDataImpl<Real> type2; + inline const ParticleDataImpl<Vec3> &getArg3() + { + return f_sec; + } + typedef ParticleDataImpl<Vec3> type3; + inline const FlagGrid &getArg4() + { + return flags; + } + typedef FlagGrid type4; + inline const MACGrid &getArg5() + { + return v; + } + typedef MACGrid type5; + inline const Grid<Real> &getArg6() + { + return neighborRatio; + } + typedef Grid<Real> type6; + inline const int &getArg7() + { + return radius; + } + typedef int type7; + inline const Vec3 &getArg8() + { + return gravity; + } + typedef Vec3 type8; + inline const Real &getArg9() + { + return k_b; + } + typedef Real type9; + inline const Real &getArg10() + { + return k_d; + } + typedef Real type10; + inline const Real &getArg11() + { + return c_s; + } + typedef Real type11; + inline const Real &getArg12() + { + return c_b; + } + typedef Real type12; + inline const Real &getArg13() + { + return dt; + } + typedef Real type13; + inline const int &getArg14() + { + return exclude; + } + typedef int type14; + inline const int &getArg15() + { + return antitunneling; + } + typedef int type15; + inline const int &getArg16() + { + return itype; + } + typedef int type16; + void runMessage() + { + debMsg("Executing kernel knFlipUpdateSecondaryParticlesCubic ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, + pts_sec, + v_sec, + l_sec, + f_sec, + flags, + v, + neighborRatio, + radius, + gravity, + k_b, + k_d, + c_s, + c_b, + dt, + exclude, + antitunneling, + itype); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystem &pts_sec; + ParticleDataImpl<Vec3> &v_sec; + ParticleDataImpl<Real> &l_sec; + const ParticleDataImpl<Vec3> &f_sec; + const FlagGrid &flags; + const MACGrid &v; + const Grid<Real> &neighborRatio; + const int radius; + const Vec3 gravity; + const Real k_b; + const Real k_d; + const Real c_s; + const Real c_b; + const Real dt; + const int exclude; + const int antitunneling; + const int itype; +}; + +void flipUpdateSecondaryParticles(const std::string mode, + BasicParticleSystem &pts_sec, + ParticleDataImpl<Vec3> &v_sec, + ParticleDataImpl<Real> &l_sec, + const ParticleDataImpl<Vec3> &f_sec, + FlagGrid &flags, + const MACGrid &v, + const Grid<Real> &neighborRatio, + const int radius, + const Vec3 gravity, + const Real k_b, + const Real k_d, + const Real c_s, + const Real c_b, + const Real dt, + const int exclude = ParticleBase::PTRACER, + const int antitunneling = 0, + const int itype = FlagGrid::TypeFluid) +{ + + Vec3 g = gravity / flags.getDx(); + if (mode == "linear") { + knFlipUpdateSecondaryParticlesLinear(pts_sec, + v_sec, + l_sec, + f_sec, + flags, + v, + neighborRatio, + g, + k_b, + k_d, + c_s, + c_b, + dt, + exclude, + antitunneling); + } + else if (mode == "cubic") { + knFlipUpdateSecondaryParticlesCubic(pts_sec, + v_sec, + l_sec, + f_sec, + flags, + v, + neighborRatio, + radius, + g, + k_b, + k_d, + c_s, + c_b, + dt, + exclude, + antitunneling, + itype); + } + else { + throw std::invalid_argument("Unknown mode: use \"linear\" or \"cubic\" instead!"); + } + pts_sec.doCompress(); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipUpdateSecondaryParticles", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const std::string mode = _args.get<std::string>("mode", 0, &_lock); + BasicParticleSystem &pts_sec = *_args.getPtr<BasicParticleSystem>("pts_sec", 1, &_lock); + ParticleDataImpl<Vec3> &v_sec = *_args.getPtr<ParticleDataImpl<Vec3>>("v_sec", 2, &_lock); + ParticleDataImpl<Real> &l_sec = *_args.getPtr<ParticleDataImpl<Real>>("l_sec", 3, &_lock); + const ParticleDataImpl<Vec3> &f_sec = *_args.getPtr<ParticleDataImpl<Vec3>>( + "f_sec", 4, &_lock); + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 5, &_lock); + const MACGrid &v = *_args.getPtr<MACGrid>("v", 6, &_lock); + const Grid<Real> &neighborRatio = *_args.getPtr<Grid<Real>>("neighborRatio", 7, &_lock); + const int radius = _args.get<int>("radius", 8, &_lock); + const Vec3 gravity = _args.get<Vec3>("gravity", 9, &_lock); + const Real k_b = _args.get<Real>("k_b", 10, &_lock); + const Real k_d = _args.get<Real>("k_d", 11, &_lock); + const Real c_s = _args.get<Real>("c_s", 12, &_lock); + const Real c_b = _args.get<Real>("c_b", 13, &_lock); + const Real dt = _args.get<Real>("dt", 14, &_lock); + const int exclude = _args.getOpt<int>("exclude", 15, ParticleBase::PTRACER, &_lock); + const int antitunneling = _args.getOpt<int>("antitunneling", 16, 0, &_lock); + const int itype = _args.getOpt<int>("itype", 17, FlagGrid::TypeFluid, &_lock); + _retval = getPyNone(); + flipUpdateSecondaryParticles(mode, + pts_sec, + v_sec, + l_sec, + f_sec, + flags, + v, + neighborRatio, + radius, + gravity, + k_b, + k_d, + c_s, + c_b, + dt, + exclude, + antitunneling, + itype); + _args.check(); + } + pbFinalizePlugin(parent, "flipUpdateSecondaryParticles", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipUpdateSecondaryParticles", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipUpdateSecondaryParticles("", + "flipUpdateSecondaryParticles", + _W_2); +extern "C" { +void PbRegister_flipUpdateSecondaryParticles() +{ + KEEP_UNUSED(_RP_flipUpdateSecondaryParticles); +} +} + +// removes secondary particles in &pts_sec that are inside boundaries (cells that are marked as +// obstacle/outflow in &flags) + +struct knFlipDeleteParticlesInObstacle : public KernelBase { + knFlipDeleteParticlesInObstacle(BasicParticleSystem &pts, const FlagGrid &flags) + : KernelBase(pts.size()), pts(pts), flags(flags) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, BasicParticleSystem &pts, const FlagGrid &flags) const + { + + if (!pts.isActive(idx)) + return; + + const Vec3 &xi = pts[idx].pos; + const Vec3i xidx = toVec3i(xi); + // remove particles that completely left the bounds + if (!flags.isInBounds(xidx)) { + pts.kill(idx); + return; + } + int gridIndex = flags.index(xidx); + // remove particles that penetrate obstacles + if (flags[gridIndex] == FlagGrid::TypeObstacle || flags[gridIndex] == FlagGrid::TypeOutflow) { + pts.kill(idx); + } + } + inline BasicParticleSystem &getArg0() + { + return pts; + } + typedef BasicParticleSystem type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + void runMessage() + { + debMsg("Executing kernel knFlipDeleteParticlesInObstacle ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, pts, flags); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystem &pts; + const FlagGrid &flags; +}; + +void flipDeleteParticlesInObstacle(BasicParticleSystem &pts, const FlagGrid &flags) +{ + + knFlipDeleteParticlesInObstacle(pts, flags); + pts.doCompress(); +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipDeleteParticlesInObstacle", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + BasicParticleSystem &pts = *_args.getPtr<BasicParticleSystem>("pts", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + _retval = getPyNone(); + flipDeleteParticlesInObstacle(pts, flags); + _args.check(); + } + pbFinalizePlugin(parent, "flipDeleteParticlesInObstacle", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipDeleteParticlesInObstacle", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipDeleteParticlesInObstacle("", + "flipDeleteParticlesInObstacle", + _W_3); +extern "C" { +void PbRegister_flipDeleteParticlesInObstacle() +{ + KEEP_UNUSED(_RP_flipDeleteParticlesInObstacle); +} +} + +// helper method to debug statistical data from grid + +void debugGridInfo(const FlagGrid &flags, + Grid<Real> &grid, + std::string name, + const int itype = FlagGrid::TypeFluid) +{ + FluidSolver *s = flags.getParent(); + int countFluid = 0; + int countLargerZero = 0; + Real avg = 0; + Real max = 0; + Real sum = 0; + Real avgLargerZero = 0; + FOR_IJK_BND(grid, 1) + { + if (!(flags(i, j, k) & itype)) + continue; + countFluid++; + if (grid(i, j, k) > 0) + countLargerZero++; + sum += grid(i, j, k); + if (grid(i, j, k) > max) + max = grid(i, j, k); + } + avg = sum / std::max(Real(countFluid), Real(1)); + avgLargerZero = sum / std::max(Real(countLargerZero), Real(1)); + + debMsg("Step: " << s->mFrame << " - Grid " << name << "\n\tcountFluid \t\t" << countFluid + << "\n\tcountLargerZero \t" << countLargerZero << "\n\tsum \t\t\t" << sum + << "\n\tavg \t\t\t" << avg << "\n\tavgLargerZero \t\t" << avgLargerZero + << "\n\tmax \t\t\t" << max, + 1); +} +static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "debugGridInfo", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &grid = *_args.getPtr<Grid<Real>>("grid", 1, &_lock); + std::string name = _args.get<std::string>("name", 2, &_lock); + const int itype = _args.getOpt<int>("itype", 3, FlagGrid::TypeFluid, &_lock); + _retval = getPyNone(); + debugGridInfo(flags, grid, name, itype); + _args.check(); + } + pbFinalizePlugin(parent, "debugGridInfo", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("debugGridInfo", e.what()); + return 0; + } +} +static const Pb::Register _RP_debugGridInfo("", "debugGridInfo", _W_4); +extern "C" { +void PbRegister_debugGridInfo() +{ + KEEP_UNUSED(_RP_debugGridInfo); +} +} + +// The following methods are helper functions to recreate the velocity and flag grid from the +// underlying FLIP simulation. They cannot simply be loaded because of the upres to a higher +// resolution, instead a levelset is used. + +struct knSetFlagsFromLevelset : public KernelBase { + knSetFlagsFromLevelset(FlagGrid &flags, + const Grid<Real> &phi, + const int exclude = FlagGrid::TypeObstacle, + const int itype = FlagGrid::TypeFluid) + : KernelBase(&flags, 0), flags(flags), phi(phi), exclude(exclude), itype(itype) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + FlagGrid &flags, + const Grid<Real> &phi, + const int exclude = FlagGrid::TypeObstacle, + const int itype = FlagGrid::TypeFluid) const + { + if (phi(idx) < 0 && !(flags(idx) & exclude)) + flags(idx) = itype; + } + inline FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const Grid<Real> &getArg1() + { + return phi; + } + typedef Grid<Real> type1; + inline const int &getArg2() + { + return exclude; + } + typedef int type2; + inline const int &getArg3() + { + return itype; + } + typedef int type3; + void runMessage() + { + debMsg("Executing kernel knSetFlagsFromLevelset ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, flags, phi, exclude, itype); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + FlagGrid &flags; + const Grid<Real> φ + const int exclude; + const int itype; +}; + +void setFlagsFromLevelset(FlagGrid &flags, + const Grid<Real> &phi, + const int exclude = FlagGrid::TypeObstacle, + const int itype = FlagGrid::TypeFluid) +{ + knSetFlagsFromLevelset(flags, phi, exclude, itype); +} +static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setFlagsFromLevelset", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + const Grid<Real> &phi = *_args.getPtr<Grid<Real>>("phi", 1, &_lock); + const int exclude = _args.getOpt<int>("exclude", 2, FlagGrid::TypeObstacle, &_lock); + const int itype = _args.getOpt<int>("itype", 3, FlagGrid::TypeFluid, &_lock); + _retval = getPyNone(); + setFlagsFromLevelset(flags, phi, exclude, itype); + _args.check(); + } + pbFinalizePlugin(parent, "setFlagsFromLevelset", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setFlagsFromLevelset", e.what()); + return 0; + } +} +static const Pb::Register _RP_setFlagsFromLevelset("", "setFlagsFromLevelset", _W_5); +extern "C" { +void PbRegister_setFlagsFromLevelset() +{ + KEEP_UNUSED(_RP_setFlagsFromLevelset); +} +} + +struct knSetMACFromLevelset : public KernelBase { + knSetMACFromLevelset(MACGrid &v, const Grid<Real> &phi, const Vec3 c) + : KernelBase(&v, 0), v(v), phi(phi), c(c) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, MACGrid &v, const Grid<Real> &phi, const Vec3 c) const + { + if (phi.getInterpolated(Vec3(i, j, k)) > 0) + v(i, j, k) = c; + } + inline MACGrid &getArg0() + { + return v; + } + typedef MACGrid type0; + inline const Grid<Real> &getArg1() + { + return phi; + } + typedef Grid<Real> type1; + inline const Vec3 &getArg2() + { + return c; + } + typedef Vec3 type2; + void runMessage() + { + debMsg("Executing kernel knSetMACFromLevelset ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, v, phi, c); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, v, phi, c); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + MACGrid &v; + const Grid<Real> φ + const Vec3 c; +}; + +void setMACFromLevelset(MACGrid &v, const Grid<Real> &phi, const Vec3 c) +{ + knSetMACFromLevelset(v, phi, c); +} +static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "setMACFromLevelset", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &v = *_args.getPtr<MACGrid>("v", 0, &_lock); + const Grid<Real> &phi = *_args.getPtr<Grid<Real>>("phi", 1, &_lock); + const Vec3 c = _args.get<Vec3>("c", 2, &_lock); + _retval = getPyNone(); + setMACFromLevelset(v, phi, c); + _args.check(); + } + pbFinalizePlugin(parent, "setMACFromLevelset", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("setMACFromLevelset", e.what()); + return 0; + } +} +static const Pb::Register _RP_setMACFromLevelset("", "setMACFromLevelset", _W_6); +extern "C" { +void PbRegister_setMACFromLevelset() +{ + KEEP_UNUSED(_RP_setMACFromLevelset); +} +} + +//---------------------------------------------------------------------------------------------------------------------------------------------------- +// END Secondary Particles for FLIP +//---------------------------------------------------------------------------------------------------------------------------------------------------- +#pragma endregion + +#pragma region Legacy Methods(still useful for debugging) +//----------------------------------------------------------------------------------------------------------------------------------- +//----------------- +// Legacy Methods (still useful for debugging) +//---------------------------------------------------------------------------------------------------------------------------------------------------- + +// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead! +// computes trapped air potential for all fluid cells in &flags and saves it in &pot + +struct knFlipComputePotentialTrappedAir : public KernelBase { + knFlipComputePotentialTrappedAir(Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const int radius, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeFluid) + : KernelBase(&pot, 1), + pot(pot), + flags(flags), + v(v), + radius(radius), + tauMin(tauMin), + tauMax(tauMax), + scaleFromManta(scaleFromManta), + itype(itype), + jtype(jtype) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const int radius, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeFluid) const + { + + if (!(flags(i, j, k) & itype)) + return; + + const Vec3 &xi = scaleFromManta * Vec3(i, j, k); // scale to unit cube + const Vec3 &vi = scaleFromManta * v.getCentered(i, j, k); + Real vdiff = 0; + for (IndexInt x = i - radius; x <= i + radius; x++) { + for (IndexInt y = j - radius; y <= j + radius; y++) { + for (IndexInt z = k - radius; z <= k + radius; z++) { + if ((x == i && y == j && z == k) || !(flags(x, y, z) & jtype)) + continue; + + const Vec3 &xj = scaleFromManta * Vec3(x, y, z); // scale to unit cube + const Vec3 &vj = scaleFromManta * v.getCentered(x, y, z); + const Vec3 xij = xi - xj; + const Vec3 vij = vi - vj; + Real h = !pot.is3D() ? 1.414 * radius : + 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius + // for h, due to squared resp. cubic neighbor area + vdiff += norm(vij) * (1 - dot(getNormalized(vij), getNormalized(xij))) * + (1 - norm(xij) / h); + } + } + } + pot(i, j, k) = (std::min(vdiff, tauMax) - std::min(vdiff, tauMin)) / (tauMax - tauMin); + } + inline Grid<Real> &getArg0() + { + return pot; + } + typedef Grid<Real> type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const MACGrid &getArg2() + { + return v; + } + typedef MACGrid type2; + inline const int &getArg3() + { + return radius; + } + typedef int type3; + inline const Real &getArg4() + { + return tauMin; + } + typedef Real type4; + inline const Real &getArg5() + { + return tauMax; + } + typedef Real type5; + inline const Real &getArg6() + { + return scaleFromManta; + } + typedef Real type6; + inline const int &getArg7() + { + return itype; + } + typedef int type7; + inline const int &getArg8() + { + return jtype; + } + typedef int type8; + void runMessage() + { + debMsg("Executing kernel knFlipComputePotentialTrappedAir ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + Grid<Real> &pot; + const FlagGrid &flags; + const MACGrid &v; + const int radius; + const Real tauMin; + const Real tauMax; + const Real scaleFromManta; + const int itype; + const int jtype; +}; + +void flipComputePotentialTrappedAir(Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const int radius, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeFluid) +{ + pot.clear(); + knFlipComputePotentialTrappedAir( + pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype); +} +static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipComputePotentialTrappedAir", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &pot = *_args.getPtr<Grid<Real>>("pot", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + const MACGrid &v = *_args.getPtr<MACGrid>("v", 2, &_lock); + const int radius = _args.get<int>("radius", 3, &_lock); + const Real tauMin = _args.get<Real>("tauMin", 4, &_lock); + const Real tauMax = _args.get<Real>("tauMax", 5, &_lock); + const Real scaleFromManta = _args.get<Real>("scaleFromManta", 6, &_lock); + const int itype = _args.getOpt<int>("itype", 7, FlagGrid::TypeFluid, &_lock); + const int jtype = _args.getOpt<int>("jtype", 8, FlagGrid::TypeFluid, &_lock); + _retval = getPyNone(); + flipComputePotentialTrappedAir( + pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype); + _args.check(); + } + pbFinalizePlugin(parent, "flipComputePotentialTrappedAir", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipComputePotentialTrappedAir", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipComputePotentialTrappedAir("", + "flipComputePotentialTrappedAir", + _W_7); +extern "C" { +void PbRegister_flipComputePotentialTrappedAir() +{ + KEEP_UNUSED(_RP_flipComputePotentialTrappedAir); +} +} + +// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead! +// computes kinetic energy potential for all fluid cells in &flags and saves it in &pot + +struct knFlipComputePotentialKineticEnergy : public KernelBase { + knFlipComputePotentialKineticEnergy(Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid) + : KernelBase(&pot, 0), + pot(pot), + flags(flags), + v(v), + tauMin(tauMin), + tauMax(tauMax), + scaleFromManta(scaleFromManta), + itype(itype) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid) const + { + + if (!(flags(i, j, k) & itype)) + return; + + const Vec3 &vi = scaleFromManta * v.getCentered(i, j, k); // scale to unit cube + Real ek = + Real(0.5) * 125 * + normSquare( + vi); // use arbitrary constant for mass, potential adjusts with thresholds anyways + pot(i, j, k) = (std::min(ek, tauMax) - std::min(ek, tauMin)) / (tauMax - tauMin); + } + inline Grid<Real> &getArg0() + { + return pot; + } + typedef Grid<Real> type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const MACGrid &getArg2() + { + return v; + } + typedef MACGrid type2; + inline const Real &getArg3() + { + return tauMin; + } + typedef Real type3; + inline const Real &getArg4() + { + return tauMax; + } + typedef Real type4; + inline const Real &getArg5() + { + return scaleFromManta; + } + typedef Real type5; + inline const int &getArg6() + { + return itype; + } + typedef int type6; + void runMessage() + { + debMsg("Executing kernel knFlipComputePotentialKineticEnergy ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, pot, flags, v, tauMin, tauMax, scaleFromManta, itype); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, pot, flags, v, tauMin, tauMax, scaleFromManta, itype); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + Grid<Real> &pot; + const FlagGrid &flags; + const MACGrid &v; + const Real tauMin; + const Real tauMax; + const Real scaleFromManta; + const int itype; +}; + +void flipComputePotentialKineticEnergy(Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid) +{ + pot.clear(); + knFlipComputePotentialKineticEnergy(pot, flags, v, tauMin, tauMax, scaleFromManta, itype); +} +static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipComputePotentialKineticEnergy", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &pot = *_args.getPtr<Grid<Real>>("pot", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + const MACGrid &v = *_args.getPtr<MACGrid>("v", 2, &_lock); + const Real tauMin = _args.get<Real>("tauMin", 3, &_lock); + const Real tauMax = _args.get<Real>("tauMax", 4, &_lock); + const Real scaleFromManta = _args.get<Real>("scaleFromManta", 5, &_lock); + const int itype = _args.getOpt<int>("itype", 6, FlagGrid::TypeFluid, &_lock); + _retval = getPyNone(); + flipComputePotentialKineticEnergy(pot, flags, v, tauMin, tauMax, scaleFromManta, itype); + _args.check(); + } + pbFinalizePlugin(parent, "flipComputePotentialKineticEnergy", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipComputePotentialKineticEnergy", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipComputePotentialKineticEnergy( + "", "flipComputePotentialKineticEnergy", _W_8); +extern "C" { +void PbRegister_flipComputePotentialKineticEnergy() +{ + KEEP_UNUSED(_RP_flipComputePotentialKineticEnergy); +} +} + +// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead! +// computes wave crest potential for all fluid cells in &flags and saves it in &pot + +struct knFlipComputePotentialWaveCrest : public KernelBase { + knFlipComputePotentialWaveCrest(Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const int radius, + Grid<Vec3> &normal, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeFluid) + : KernelBase(&pot, 1), + pot(pot), + flags(flags), + v(v), + radius(radius), + normal(normal), + tauMin(tauMin), + tauMax(tauMax), + scaleFromManta(scaleFromManta), + itype(itype), + jtype(jtype) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const int radius, + Grid<Vec3> &normal, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeFluid) const + { + + if (!(flags(i, j, k) & itype)) + return; + + const Vec3 &xi = scaleFromManta * Vec3(i, j, k); // scale to unit cube + const Vec3 &vi = scaleFromManta * v.getCentered(i, j, k); + const Vec3 &ni = normal(i, j, k); + Real kappa = 0; + for (IndexInt x = i - radius; x <= i + radius; x++) { + for (IndexInt y = j - radius; y <= j + radius; y++) { + for (IndexInt z = k - radius; z <= k + radius; z++) { + if ((x == i && y == j && z == k) || !(flags(x, y, z) & jtype)) + continue; + const Vec3 &xj = scaleFromManta * Vec3(x, y, z); // scale to unit cube + const Vec3 &nj = normal(x, y, z); + const Vec3 xij = xi - xj; + if (dot(getNormalized(xij), ni) < 0) { // identifies wave crests + Real h = !pot.is3D() ? + 1.414 * radius : + 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius for h, + // due to squared resp. cubic neighbor area + kappa += (1 - dot(ni, nj)) * (1 - norm(xij) / h); + } + } + } + } + + if (dot(getNormalized(vi), ni) >= 0.6) { // avoid to mark boarders of the scene as wave crest + pot(i, j, k) = (std::min(kappa, tauMax) - std::min(kappa, tauMin)) / (tauMax - tauMin); + } + else { + pot(i, j, k) = Real(0); + } + } + inline Grid<Real> &getArg0() + { + return pot; + } + typedef Grid<Real> type0; + inline const FlagGrid &getArg1() + { + return flags; + } + typedef FlagGrid type1; + inline const MACGrid &getArg2() + { + return v; + } + typedef MACGrid type2; + inline const int &getArg3() + { + return radius; + } + typedef int type3; + inline Grid<Vec3> &getArg4() + { + return normal; + } + typedef Grid<Vec3> type4; + inline const Real &getArg5() + { + return tauMin; + } + typedef Real type5; + inline const Real &getArg6() + { + return tauMax; + } + typedef Real type6; + inline const Real &getArg7() + { + return scaleFromManta; + } + typedef Real type7; + inline const int &getArg8() + { + return itype; + } + typedef int type8; + inline const int &getArg9() + { + return jtype; + } + typedef int type9; + void runMessage() + { + debMsg("Executing kernel knFlipComputePotentialWaveCrest ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, + j, + k, + pot, + flags, + v, + radius, + normal, + tauMin, + tauMax, + scaleFromManta, + itype, + jtype); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, pot, flags, v, radius, normal, tauMin, tauMax, scaleFromManta, itype, jtype); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + Grid<Real> &pot; + const FlagGrid &flags; + const MACGrid &v; + const int radius; + Grid<Vec3> &normal; + const Real tauMin; + const Real tauMax; + const Real scaleFromManta; + const int itype; + const int jtype; +}; + +void flipComputePotentialWaveCrest(Grid<Real> &pot, + const FlagGrid &flags, + const MACGrid &v, + const int radius, + Grid<Vec3> &normal, + const Real tauMin, + const Real tauMax, + const Real scaleFromManta, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeFluid) +{ + + pot.clear(); + knFlipComputePotentialWaveCrest( + pot, flags, v, radius, normal, tauMin, tauMax, scaleFromManta, itype, jtype); +} +static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipComputePotentialWaveCrest", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &pot = *_args.getPtr<Grid<Real>>("pot", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + const MACGrid &v = *_args.getPtr<MACGrid>("v", 2, &_lock); + const int radius = _args.get<int>("radius", 3, &_lock); + Grid<Vec3> &normal = *_args.getPtr<Grid<Vec3>>("normal", 4, &_lock); + const Real tauMin = _args.get<Real>("tauMin", 5, &_lock); + const Real tauMax = _args.get<Real>("tauMax", 6, &_lock); + const Real scaleFromManta = _args.get<Real>("scaleFromManta", 7, &_lock); + const int itype = _args.getOpt<int>("itype", 8, FlagGrid::TypeFluid, &_lock); + const int jtype = _args.getOpt<int>("jtype", 9, FlagGrid::TypeFluid, &_lock); + _retval = getPyNone(); + flipComputePotentialWaveCrest( + pot, flags, v, radius, normal, tauMin, tauMax, scaleFromManta, itype, jtype); + _args.check(); + } + pbFinalizePlugin(parent, "flipComputePotentialWaveCrest", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipComputePotentialWaveCrest", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipComputePotentialWaveCrest("", + "flipComputePotentialWaveCrest", + _W_9); +extern "C" { +void PbRegister_flipComputePotentialWaveCrest() +{ + KEEP_UNUSED(_RP_flipComputePotentialWaveCrest); +} +} + +// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead! +// computes normal grid &normal as gradient of levelset &phi and normalizes it + +struct knFlipComputeSurfaceNormals : public KernelBase { + knFlipComputeSurfaceNormals(Grid<Vec3> &normal, const Grid<Real> &phi) + : KernelBase(&normal, 0), normal(normal), phi(phi) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, Grid<Vec3> &normal, const Grid<Real> &phi) const + { + normal[idx] = getNormalized(normal[idx]); + } + inline Grid<Vec3> &getArg0() + { + return normal; + } + typedef Grid<Vec3> type0; + inline const Grid<Real> &getArg1() + { + return phi; + } + typedef Grid<Real> type1; + void runMessage() + { + debMsg("Executing kernel knFlipComputeSurfaceNormals ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, normal, phi); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + Grid<Vec3> &normal; + const Grid<Real> φ +}; + +void flipComputeSurfaceNormals(Grid<Vec3> &normal, const Grid<Real> &phi) +{ + GradientOp(normal, phi); + knFlipComputeSurfaceNormals(normal, phi); +} +static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipComputeSurfaceNormals", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Vec3> &normal = *_args.getPtr<Grid<Vec3>>("normal", 0, &_lock); + const Grid<Real> &phi = *_args.getPtr<Grid<Real>>("phi", 1, &_lock); + _retval = getPyNone(); + flipComputeSurfaceNormals(normal, phi); + _args.check(); + } + pbFinalizePlugin(parent, "flipComputeSurfaceNormals", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipComputeSurfaceNormals", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipComputeSurfaceNormals("", "flipComputeSurfaceNormals", _W_10); +extern "C" { +void PbRegister_flipComputeSurfaceNormals() +{ + KEEP_UNUSED(_RP_flipComputeSurfaceNormals); +} +} + +// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead! +// computes the neighbor ratio for every fluid cell in &flags as the number of fluid neighbors over +// the maximum possible number of fluid neighbors + +struct knFlipUpdateNeighborRatio : public KernelBase { + knFlipUpdateNeighborRatio(const FlagGrid &flags, + Grid<Real> &neighborRatio, + const int radius, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeObstacle) + : KernelBase(&flags, 1), + flags(flags), + neighborRatio(neighborRatio), + radius(radius), + itype(itype), + jtype(jtype) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &neighborRatio, + const int radius, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeObstacle) const + { + + if (!(flags(i, j, k) & itype)) + return; + + int countFluid = 0; + int countMaxFluid = 0; + for (IndexInt x = i - radius; x <= i + radius; x++) { + for (IndexInt y = j - radius; y <= j + radius; y++) { + for (IndexInt z = k - radius; z <= k + radius; z++) { + if ((x == i && y == j && z == k) || (flags(x, y, z) & jtype)) + continue; + if (flags(x, y, z) & itype) { + countFluid++; + countMaxFluid++; + } + else { + countMaxFluid++; + } + } + } + } + neighborRatio(i, j, k) = float(countFluid) / float(countMaxFluid); + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return neighborRatio; + } + typedef Grid<Real> type1; + inline const int &getArg2() + { + return radius; + } + typedef int type2; + inline const int &getArg3() + { + return itype; + } + typedef int type3; + inline const int &getArg4() + { + return jtype; + } + typedef int type4; + void runMessage() + { + debMsg("Executing kernel knFlipUpdateNeighborRatio ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, neighborRatio, radius, itype, jtype); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, neighborRatio, radius, itype, jtype); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> &neighborRatio; + const int radius; + const int itype; + const int jtype; +}; + +void flipUpdateNeighborRatio(const FlagGrid &flags, + Grid<Real> &neighborRatio, + const int radius, + const int itype = FlagGrid::TypeFluid, + const int jtype = FlagGrid::TypeObstacle) +{ + + neighborRatio.clear(); + knFlipUpdateNeighborRatio(flags, neighborRatio, radius, itype, jtype); +} +static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "flipUpdateNeighborRatio", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &neighborRatio = *_args.getPtr<Grid<Real>>("neighborRatio", 1, &_lock); + const int radius = _args.get<int>("radius", 2, &_lock); + const int itype = _args.getOpt<int>("itype", 3, FlagGrid::TypeFluid, &_lock); + const int jtype = _args.getOpt<int>("jtype", 4, FlagGrid::TypeObstacle, &_lock); + _retval = getPyNone(); + flipUpdateNeighborRatio(flags, neighborRatio, radius, itype, jtype); + _args.check(); + } + pbFinalizePlugin(parent, "flipUpdateNeighborRatio", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("flipUpdateNeighborRatio", e.what()); + return 0; + } +} +static const Pb::Register _RP_flipUpdateNeighborRatio("", "flipUpdateNeighborRatio", _W_11); +extern "C" { +void PbRegister_flipUpdateNeighborRatio() +{ + KEEP_UNUSED(_RP_flipUpdateNeighborRatio); +} +} + +//---------------------------------------------------------------------------------------------------------------------------------------------------- +// Legacy Methods (still useful for debugging) +//---------------------------------------------------------------------------------------------------------------------------------------------------- +#pragma endregion + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/surfaceturbulence.cpp b/extern/mantaflow/preprocessed/plugin/surfaceturbulence.cpp new file mode 100644 index 00000000000..465314f51ed --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/surfaceturbulence.cpp @@ -0,0 +1,2189 @@ + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2016 Olivier Mercier, oli.mercier@gmail.com + * + * This program is free software, distributed under the terms of the + * Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Surface Turbulence for Particle-Based Liquid Simulations + * Mercier et al., SIGGRAPH Asia 2015 + * + * Possible speedups : + * - only initialize surface points around coarse particles near the surface. Use the flags in the + *fluid grid and only use cells with non-fluid neighbors. + * + ******************************************************************************/ + +// use chrono stl for detailed timing only if available +#ifdef __GNUC__ +# if __GNUC__ < 5 +# define USE_CHRONO 0 +# endif +#endif + +#if MANTA_WITHCPP11 == 1 +# ifndef USE_CHRONO +# define USE_CHRONO 1 +# endif +#endif + +#include <iomanip> +#if USE_CHRONO == 1 +# include <chrono> +#endif +#include "particle.h" + +using namespace std; +namespace Manta { + +// own namespace for globals +namespace SurfaceTurbulence { + +// +// **** surface turbulence parameters **** +// +struct SurfaceTurbulenceParameters { + int res; + Real outerRadius; + int surfaceDensity; + int nbSurfaceMaintenanceIterations; + Real dt; + Real waveSpeed; + Real waveDamping; + Real waveSeedFrequency; + Real waveMaxAmplitude; + Real waveMaxFrequency; + Real waveMaxSeedingAmplitude; // as ratio of max amp; + Real waveSeedingCurvatureThresholdRegionCenter; + Real waveSeedingCurvatureThresholdRegionRadius; + Real waveSeedStepSizeRatioOfMax; + Real innerRadius; + Real meanFineDistance; + Real constraintA; + Real normalRadius; + Real tangentRadius; + Real bndXm, bndXp, bndYm, bndYp, bndZm, bndZp; +}; +SurfaceTurbulenceParameters params; + +// +// **** acceleration grid for particle neighbor queries **** +// +struct ParticleAccelGrid { + int res; + vector<int> ***indices; + + void init(int inRes) + { + res = inRes; + indices = new vector<int> **[res]; + for (int i = 0; i < res; i++) { + indices[i] = new vector<int> *[res]; + for (int j = 0; j < res; j++) { + indices[i][j] = new vector<int>[res]; + } + } + } + + void fillWith(const BasicParticleSystem &particles) + { + // clear + for (int i = 0; i < res; i++) { + for (int j = 0; j < res; j++) { + for (int k = 0; k < res; k++) { + indices[i][j][k].clear(); + } + } + } + + // fill + for (int id = 0; id < particles.size(); id++) { + Vec3 pos = particles.getPos(id); + int i = clamp<int>(floor(pos.x / params.res * res), 0, res - 1); + int j = clamp<int>(floor(pos.y / params.res * res), 0, res - 1); + int k = clamp<int>(floor(pos.z / params.res * res), 0, res - 1); + indices[i][j][k].push_back(id); + } + } + + void fillWith(const ParticleDataImpl<Vec3> &particles) + { + // clear + for (int i = 0; i < res; i++) { + for (int j = 0; j < res; j++) { + for (int k = 0; k < res; k++) { + indices[i][j][k].clear(); + } + } + } + + // fill + for (int id = 0; id < particles.size(); id++) { + Vec3 pos = particles[id]; + int i = clamp<int>(floor(pos.x / params.res * res), 0, res - 1); + int j = clamp<int>(floor(pos.y / params.res * res), 0, res - 1); + int k = clamp<int>(floor(pos.z / params.res * res), 0, res - 1); + indices[i][j][k].push_back(id); + } + } +}; + +#define LOOP_NEIGHBORS_BEGIN(points, center, radius) \ + int minI = clamp<int>( \ + floor((center.x - radius) / params.res * points.accel->res), 0, points.accel->res - 1); \ + int maxI = clamp<int>( \ + floor((center.x + radius) / params.res * points.accel->res), 0, points.accel->res - 1); \ + int minJ = clamp<int>( \ + floor((center.y - radius) / params.res * points.accel->res), 0, points.accel->res - 1); \ + int maxJ = clamp<int>( \ + floor((center.y + radius) / params.res * points.accel->res), 0, points.accel->res - 1); \ + int minK = clamp<int>( \ + floor((center.z - radius) / params.res * points.accel->res), 0, points.accel->res - 1); \ + int maxK = clamp<int>( \ + floor((center.z + radius) / params.res * points.accel->res), 0, points.accel->res - 1); \ + for (int i = minI; i <= maxI; i++) { \ + for (int j = minJ; j <= maxJ; j++) { \ + for (int k = minK; k <= maxK; k++) { \ + for (int idLOOPNEIGHBORS = 0; \ + idLOOPNEIGHBORS < (int)points.accel->indices[i][j][k].size(); \ + idLOOPNEIGHBORS++) { \ + int idn = points.accel->indices[i][j][k][idLOOPNEIGHBORS]; \ + if (points.isActive(idn)) { +#define LOOP_NEIGHBORS_END \ + } \ + } \ + } \ + } \ + } + +#define LOOP_GHOSTS_POS_BEGIN(pos, radius) \ + int flagLOOPGHOSTS = -1; \ + Vec3 gPos; \ + while (flagLOOPGHOSTS < 6) { \ + if (flagLOOPGHOSTS < 0 && pos.x - params.bndXm <= radius) { \ + flagLOOPGHOSTS = 0; \ + gPos = Vec3(2.f * params.bndXm - pos.x, pos.y, pos.z); \ + } \ + else if (flagLOOPGHOSTS < 1 && params.bndXp - pos.x <= radius) { \ + flagLOOPGHOSTS = 1; \ + gPos = Vec3(2.f * params.bndXp - pos.x, pos.y, pos.z); \ + } \ + else if (flagLOOPGHOSTS < 2 && pos.y - params.bndYm <= radius) { \ + flagLOOPGHOSTS = 2; \ + gPos = Vec3(pos.x, 2.f * params.bndYm - pos.y, pos.z); \ + } \ + else if (flagLOOPGHOSTS < 3 && params.bndYp - pos.y <= radius) { \ + flagLOOPGHOSTS = 3; \ + gPos = Vec3(pos.x, 2.f * params.bndYp - pos.y, pos.z); \ + } \ + else if (flagLOOPGHOSTS < 4 && pos.z - params.bndZm <= radius) { \ + flagLOOPGHOSTS = 4; \ + gPos = Vec3(pos.x, pos.y, 2.f * params.bndZm - pos.z); \ + } \ + else if (flagLOOPGHOSTS < 5 && params.bndZp - pos.Z <= radius) { \ + flagLOOPGHOSTS = 5; \ + gPos = Vec3(pos.x, pos.y, 2.f * params.bndZp - pos.z); \ + } \ + else { \ + flagLOOPGHOSTS = 6; \ + gPos = Vec3(pos.x, pos.y, pos.z); \ + } +#define LOOP_GHOSTS_POS_NORMAL_BEGIN(pos, normal, radius) \ + int flagLOOPGHOSTS = -1; \ + Vec3 gPos, gNormal; \ + while (flagLOOPGHOSTS < 6) { \ + if (flagLOOPGHOSTS < 0 && pos.x - params.bndXm <= radius) { \ + flagLOOPGHOSTS = 0; \ + gPos = Vec3(2.f * params.bndXm - pos.x, pos.y, pos.z); \ + gNormal = Vec3(-normal.x, normal.y, normal.z); \ + } \ + else if (flagLOOPGHOSTS < 1 && params.bndXp - pos.x <= radius) { \ + flagLOOPGHOSTS = 1; \ + gPos = Vec3(2.f * params.bndXp - pos.x, pos.y, pos.z); \ + gNormal = Vec3(-normal.x, normal.y, normal.z); \ + } \ + else if (flagLOOPGHOSTS < 2 && pos.y - params.bndYm <= radius) { \ + flagLOOPGHOSTS = 2; \ + gPos = Vec3(pos.x, 2.f * params.bndYm - pos.y, pos.z); \ + gNormal = Vec3(normal.x, -normal.y, normal.z); \ + } \ + else if (flagLOOPGHOSTS < 3 && params.bndYp - pos.y <= radius) { \ + flagLOOPGHOSTS = 3; \ + gPos = Vec3(pos.x, 2.f * params.bndYp - pos.y, pos.z); \ + gNormal = Vec3(normal.x, -normal.y, normal.z); \ + } \ + else if (flagLOOPGHOSTS < 4 && pos.z - params.bndZm <= radius) { \ + flagLOOPGHOSTS = 4; \ + gPos = Vec3(pos.x, pos.y, 2.f * params.bndZm - pos.z); \ + gNormal = Vec3(normal.x, normal.y, -normal.z); \ + } \ + else if (flagLOOPGHOSTS < 5 && params.bndZp - pos.Z <= radius) { \ + flagLOOPGHOSTS = 5; \ + gPos = Vec3(pos.x, pos.y, 2.f * params.bndZp - pos.z); \ + gNormal = Vec3(normal.x, normal.y, -normal.z); \ + } \ + else { \ + flagLOOPGHOSTS = 6; \ + gPos = pos; \ + gNormal = normal; \ + } +#define LOOP_GHOSTS_END } + +// +// **** Wrappers around point sets to attach it an acceleration grid **** +// +struct PointSetWrapper { + ParticleAccelGrid *accel; + + PointSetWrapper(ParticleAccelGrid *inAccel) + { + accel = inAccel; + } + virtual void updateAccel() = 0; +}; + +struct BasicParticleSystemWrapper : PointSetWrapper { + BasicParticleSystem *points; + + BasicParticleSystemWrapper(ParticleAccelGrid *inAccel) : PointSetWrapper(inAccel) + { + } + + Vec3 getPos(int id) const + { + return points->getPos(id); + } + void setPos(int id, Vec3 pos) + { + points->setPos(id, pos); + } + void updateAccel() + { + accel->fillWith(*points); + } + void clear() + { + points->clear(); + } + int size() const + { + return points->size(); + } + bool isActive(int id) const + { + return points->isActive(id); + } + void addParticle(Vec3 pos) + { + points->addParticle(pos); + } + int getStatus(int id) const + { + return points->getStatus(id); + } + void addBuffered(Vec3 pos) + { + points->addBuffered(pos); + } + void doCompress() + { + points->doCompress(); + } + void insertBufferedParticles() + { + points->insertBufferedParticles(); + } + void kill(int id) + { + points->kill(id); + } + + bool hasNeighbor(Vec3 pos, Real radius) const + { + bool answer = false; + int minI = clamp<int>(floor((pos.x - radius) / params.res * accel->res), 0, accel->res - 1); + int maxI = clamp<int>(floor((pos.x + radius) / params.res * accel->res), 0, accel->res - 1); + int minJ = clamp<int>(floor((pos.y - radius) / params.res * accel->res), 0, accel->res - 1); + int maxJ = clamp<int>(floor((pos.y + radius) / params.res * accel->res), 0, accel->res - 1); + int minK = clamp<int>(floor((pos.z - radius) / params.res * accel->res), 0, accel->res - 1); + int maxK = clamp<int>(floor((pos.z + radius) / params.res * accel->res), 0, accel->res - 1); + for (int i = minI; i <= maxI; i++) { + for (int j = minJ; j <= maxJ; j++) { + for (int k = minK; k <= maxK; k++) { + for (int id = 0; id < (int)accel->indices[i][j][k].size(); id++) { + if (points->isActive(accel->indices[i][j][k][id]) && + norm(points->getPos(accel->indices[i][j][k][id]) - pos) <= radius) { + answer = true; + break; + } + } + if (answer) + break; + } + if (answer) + break; + } + if (answer) + break; + } + return answer; + } + + bool hasNeighborOtherThanItself(int idx, Real radius) const + { + bool answer = false; + Vec3 pos = points->getPos(idx); + int minI = clamp<int>(floor((pos.x - radius) / params.res * accel->res), 0, accel->res - 1); + int maxI = clamp<int>(floor((pos.x + radius) / params.res * accel->res), 0, accel->res - 1); + int minJ = clamp<int>(floor((pos.y - radius) / params.res * accel->res), 0, accel->res - 1); + int maxJ = clamp<int>(floor((pos.y + radius) / params.res * accel->res), 0, accel->res - 1); + int minK = clamp<int>(floor((pos.z - radius) / params.res * accel->res), 0, accel->res - 1); + int maxK = clamp<int>(floor((pos.z + radius) / params.res * accel->res), 0, accel->res - 1); + for (int i = minI; i <= maxI; i++) { + for (int j = minJ; j <= maxJ; j++) { + for (int k = minK; k <= maxK; k++) { + for (int id = 0; id < (int)accel->indices[i][j][k].size(); id++) { + if (accel->indices[i][j][k][id] != idx && + points->isActive(accel->indices[i][j][k][id]) && + norm(points->getPos(accel->indices[i][j][k][id]) - pos) <= radius) { + answer = true; + break; + } + } + if (answer) + break; + } + if (answer) + break; + } + if (answer) + break; + } + return answer; + } + + void removeInvalidIndices(vector<int> &indices) + { + vector<int> copy; + copy.resize(indices.size()); + for (int i = 0; i < (int)indices.size(); i++) { + copy[i] = indices[i]; + } + indices.clear(); + for (int i = 0; i < (int)copy.size(); i++) { + if (points->isActive(copy[i])) { + indices.push_back(copy[i]); + } + } + } +}; + +struct ParticleDataImplVec3Wrapper : PointSetWrapper { + ParticleDataImpl<Vec3> *points; + + ParticleDataImplVec3Wrapper(ParticleAccelGrid *inAccel) : PointSetWrapper(inAccel) + { + } + + Vec3 getVec3(int id) const + { + return (*points)[id]; + } + void setVec3(int id, Vec3 vec) + { + (*points)[id] = vec; + } + void updateAccel() + { + accel->fillWith(*points); + } + bool isActive(int i) const + { + return true; + } +}; + +// +// **** globals **** +// +ParticleAccelGrid accelCoarse, accelSurface; +BasicParticleSystemWrapper coarseParticles(&accelCoarse), surfacePoints(&accelSurface); +ParticleDataImplVec3Wrapper coarseParticlesPrevPos( + &accelCoarse); // WARNING: reusing the coarse accel grid to save space, don't query + // coarseParticlesPrevPos and coarseParticles at the same time. +vector<Vec3> tempSurfaceVec3; // to store misc info on surface points +vector<Real> tempSurfaceFloat; // to store misc info on surface points +int frameCount = 0; + +// +//**** weighting kernels ***** +// +Real triangularWeight(Real distance, Real radius) +{ + return 1.0f - distance / radius; +} +Real exponentialWeight(Real distance, Real radius, Real falloff) +{ + if (distance > radius) + return 0; + Real tmp = distance / radius; + return expf(-falloff * tmp * tmp); +} + +Real weightKernelAdvection(Real distance) +{ + if (distance > 2.f * params.outerRadius) { + return 0; + } + else { + return triangularWeight(distance, 2.f * params.outerRadius); + } +} + +Real weightKernelCoarseDensity(Real distance) +{ + return exponentialWeight(distance, params.outerRadius, 2.0f); +} + +Real weightSurfaceNormal(Real distance) +{ + if (distance > params.normalRadius) { + return 0; + } + else { + return triangularWeight(distance, params.normalRadius); + } +} + +Real weightSurfaceTangent(Real distance) +{ + if (distance > params.tangentRadius) { + return 0; + } + else { + return triangularWeight(distance, params.tangentRadius); + } +} + +// +// **** utility **** +// + +bool isInDomain(Vec3 pos) +{ + return params.bndXm <= pos.x && pos.x <= params.bndXp && params.bndYm <= pos.y && + pos.y <= params.bndYp && params.bndZm <= pos.z && pos.z <= params.bndZp; +} + +Real smoothstep(Real edgeLeft, Real edgeRight, Real val) +{ + Real x = clamp((val - edgeLeft) / (edgeRight - edgeLeft), Real(0.), Real(1.)); + return x * x * (3 - 2 * x); +} + +// +// **** surface initialization **** +// + +void initFines(const BasicParticleSystemWrapper &coarseParticles, + BasicParticleSystemWrapper &surfacePoints, + const FlagGrid &flags) +{ + unsigned int discretization = (unsigned int)M_PI * (params.outerRadius + params.innerRadius) / + params.meanFineDistance; + Real dtheta = 2 * params.meanFineDistance / (params.outerRadius + params.innerRadius); + Real outerRadius2 = params.outerRadius * params.outerRadius; + + surfacePoints.clear(); + for (int idx = 0; idx < (int)coarseParticles.size(); idx++) { + + if (idx % 500 == 0) { + cout << "Initializing surface points : " << setprecision(4) + << 100.f * idx / coarseParticles.size() << "%" << endl; + } + + if (coarseParticles.isActive(idx)) { + + // check flags if we are near surface + bool nearSurface = false; + Vec3 pos = coarseParticles.getPos(idx); + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + if (!flags.isFluid(((int)pos.x) + i, ((int)pos.y) + j, ((int)pos.z) + k)) { + nearSurface = true; + break; + } + } + } + } + + if (nearSurface) { + for (unsigned int i = 0; i <= discretization / 2; ++i) { + Real discretization2 = Real(floor(2 * M_PI * sin(i * dtheta) / dtheta) + 1); + for (Real phi = 0; phi < 2 * M_PI; phi += Real(2 * M_PI / discretization2)) { + Real theta = i * dtheta; + Vec3 normal(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi)); + Vec3 position = coarseParticles.getPos(idx) + params.outerRadius * normal; + + bool valid = true; + LOOP_NEIGHBORS_BEGIN(coarseParticles, position, 2.f * params.outerRadius) + if (idx != idn && normSquare(position - coarseParticles.getPos(idn)) < outerRadius2) { + valid = false; + break; + } + LOOP_NEIGHBORS_END + if (valid) { + surfacePoints.addParticle(position); + } + } + } + } + } + } +} + +// +// **** surface advection **** +// + +struct advectSurfacePoints : public KernelBase { + advectSurfacePoints(BasicParticleSystemWrapper &surfacePoints, + const BasicParticleSystemWrapper &coarseParticles, + const ParticleDataImplVec3Wrapper &coarseParticlesPrevPos) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + coarseParticles(coarseParticles), + coarseParticlesPrevPos(coarseParticlesPrevPos) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + BasicParticleSystemWrapper &surfacePoints, + const BasicParticleSystemWrapper &coarseParticles, + const ParticleDataImplVec3Wrapper &coarseParticlesPrevPos) const + { + if (surfacePoints.isActive(idx)) { + Vec3 avgDisplacement(0, 0, 0); + Real totalWeight = 0; + Vec3 p = surfacePoints.getPos(idx); + LOOP_NEIGHBORS_BEGIN( + coarseParticlesPrevPos, surfacePoints.getPos(idx), 2.0f * params.outerRadius) + if ((coarseParticles.getStatus(idn) & ParticleBase::PNEW) == 0 && + (coarseParticles.getStatus(idn) & ParticleBase::PDELETE) == 0) { + Vec3 disp = coarseParticles.getPos(idn) - coarseParticlesPrevPos.getVec3(idn); + Real distance = norm(coarseParticlesPrevPos.getVec3(idn) - p); + Real w = weightKernelAdvection(distance); + avgDisplacement += w * disp; + totalWeight += w; + } + LOOP_NEIGHBORS_END + if (totalWeight != 0) + avgDisplacement /= totalWeight; + surfacePoints.setPos(idx, p + avgDisplacement); + } + } + inline BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const BasicParticleSystemWrapper &getArg1() + { + return coarseParticles; + } + typedef BasicParticleSystemWrapper type1; + inline const ParticleDataImplVec3Wrapper &getArg2() + { + return coarseParticlesPrevPos; + } + typedef ParticleDataImplVec3Wrapper type2; + void runMessage() + { + debMsg("Executing kernel advectSurfacePoints ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, coarseParticles, coarseParticlesPrevPos); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystemWrapper &surfacePoints; + const BasicParticleSystemWrapper &coarseParticles; + const ParticleDataImplVec3Wrapper &coarseParticlesPrevPos; +}; + +// +// **** value and gradient of level-set band constraint **** +// +Real computeConstraintLevel(const BasicParticleSystemWrapper &coarseParticles, Vec3 pos) +{ + Real lvl = 0.0f; + LOOP_NEIGHBORS_BEGIN(coarseParticles, pos, 1.5f * params.outerRadius) + lvl += expf(-params.constraintA * normSquare(coarseParticles.getPos(idn) - pos)); + LOOP_NEIGHBORS_END + if (lvl > 1.0f) + lvl = 1.0f; + lvl = (sqrtf(-logf(lvl) / params.constraintA) - params.innerRadius) / + (params.outerRadius - params.innerRadius); + return lvl; +} + +Vec3 computeConstraintGradient(const BasicParticleSystemWrapper &coarseParticles, Vec3 pos) +{ + Vec3 gradient(0, 0, 0); + LOOP_NEIGHBORS_BEGIN(coarseParticles, pos, 1.5f * params.outerRadius) + gradient += 2.f * params.constraintA * + (Real)(expf(-params.constraintA * normSquare(coarseParticles.getPos(idn) - pos))) * + (pos - coarseParticles.getPos(idn)); + LOOP_NEIGHBORS_END + return getNormalized(gradient); +} + +// +// **** compute surface normals **** +// + +struct computeSurfaceNormals : public KernelBase { + computeSurfaceNormals(const BasicParticleSystemWrapper &surfacePoints, + const BasicParticleSystemWrapper &coarseParticles, + ParticleDataImpl<Vec3> &surfaceNormals) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + coarseParticles(coarseParticles), + surfaceNormals(surfaceNormals) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + const BasicParticleSystemWrapper &coarseParticles, + ParticleDataImpl<Vec3> &surfaceNormals) const + { + Vec3 pos = surfacePoints.getPos(idx); + + // approx normal with gradient + Vec3 gradient = computeConstraintGradient(coarseParticles, pos); + + // get tangent frame + Vec3 n = getNormalized(gradient); + Vec3 vx(1, 0, 0); + Vec3 vy(0, 1, 0); + Real dotX = dot(n, vx); + Real dotY = dot(n, vy); + Vec3 t1 = getNormalized(fabs(dotX) < fabs(dotY) ? cross(n, vx) : cross(n, vy)); + Vec3 t2 = getNormalized(cross(n, t1)); // initial frame + + // linear fit of neighboring surface points in approximated tangent frame + Real sw = 0, swx = 0, swy = 0, swxy = 0, swx2 = 0, swy2 = 0, swxz = 0, swyz = 0, swz = 0; + LOOP_NEIGHBORS_BEGIN(surfacePoints, pos, params.normalRadius) + LOOP_GHOSTS_POS_BEGIN(surfacePoints.getPos(idn), params.normalRadius) + Real x = dot(gPos - pos, t1); + Real y = dot(gPos - pos, t2); + Real z = dot(gPos - pos, n); + Real w = weightSurfaceNormal(norm(pos - gPos)); + swx2 += w * x * x; + swy2 += w * y * y; + swxy += w * x * y; + swxz += w * x * z; + swyz += w * y * z; + swx += w * x; + swy += w * y; + swz += w * z; + sw += w; + LOOP_GHOSTS_END + LOOP_NEIGHBORS_END + Real det = -sw * swxy * swxy + 2.f * swx * swxy * swy - swx2 * swy * swy - swx * swx * swy2 + + sw * swx2 * swy2; + if (det == 0) { + surfaceNormals[idx] = Vec3(0, 0, 0); + } + else { + Vec3 abc = 1.f / det * + Vec3(swxz * (-swy * swy + sw * swy2) + swyz * (-sw * swxy + swx * swy) + + swz * (swxy * swy - swx * swy2), + swxz * (-sw * swxy + swx * swy) + swyz * (-swx * swx + sw * swx2) + + swz * (swx * swxy - swx2 * swy), + swxz * (swxy * swy - swx * swy2) + swyz * (swx * swxy - swx2 * swy) + + swz * (-swxy * swxy + swx2 * swy2)); + Vec3 normal = -getNormalized(t1 * abc.x + t2 * abc.y - n); + if (dot(gradient, normal) < 0) { + normal = -normal; + } + surfaceNormals[idx] = normal; + } + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const BasicParticleSystemWrapper &getArg1() + { + return coarseParticles; + } + typedef BasicParticleSystemWrapper type1; + inline ParticleDataImpl<Vec3> &getArg2() + { + return surfaceNormals; + } + typedef ParticleDataImpl<Vec3> type2; + void runMessage() + { + debMsg("Executing kernel computeSurfaceNormals ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, coarseParticles, surfaceNormals); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + const BasicParticleSystemWrapper &coarseParticles; + ParticleDataImpl<Vec3> &surfaceNormals; +}; + +// +// **** smooth surface normals **** +// + +struct computeAveragedNormals : public KernelBase { + computeAveragedNormals(const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceNormals(surfaceNormals) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals) const + { + Vec3 pos = surfacePoints.getPos(idx); + Vec3 newNormal = Vec3(0, 0, 0); + LOOP_NEIGHBORS_BEGIN(surfacePoints, pos, params.normalRadius) + Real w = weightSurfaceNormal(norm(pos - surfacePoints.getPos(idn))); + newNormal += w * surfaceNormals[idn]; + LOOP_NEIGHBORS_END + tempSurfaceVec3[idx] = getNormalized(newNormal); + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const ParticleDataImpl<Vec3> &getArg1() + { + return surfaceNormals; + } + typedef ParticleDataImpl<Vec3> type1; + void runMessage() + { + debMsg("Executing kernel computeAveragedNormals ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceNormals); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + const ParticleDataImpl<Vec3> &surfaceNormals; +}; + +struct assignNormals : public KernelBase { + assignNormals(const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Vec3> &surfaceNormals) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceNormals(surfaceNormals) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Vec3> &surfaceNormals) const + { + surfaceNormals[idx] = tempSurfaceVec3[idx]; + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline ParticleDataImpl<Vec3> &getArg1() + { + return surfaceNormals; + } + typedef ParticleDataImpl<Vec3> type1; + void runMessage() + { + debMsg("Executing kernel assignNormals ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceNormals); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + ParticleDataImpl<Vec3> &surfaceNormals; +}; + +void smoothSurfaceNormals(const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Vec3> &surfaceNormals) +{ + tempSurfaceVec3.resize(surfacePoints.size()); + + computeAveragedNormals(surfacePoints, surfaceNormals); + assignNormals(surfacePoints, surfaceNormals); +} + +// +// **** addition/deletion of particles. Not parallel to prevent write/delete conflicts **** +// + +void addDeleteSurfacePoints(BasicParticleSystemWrapper &surfacePoints) +{ + int fixedSize = surfacePoints.size(); + for (int idx = 0; idx < fixedSize; idx++) { + // compute proxy tangent displacement + Vec3 pos = surfacePoints.getPos(idx); + + Vec3 gradient = computeConstraintGradient(coarseParticles, pos); + + Real wt = 0; + Vec3 tangentDisplacement(0, 0, 0); + LOOP_NEIGHBORS_BEGIN(surfacePoints, pos, params.tangentRadius) + if (idn != idx) { + Vec3 dir = pos - surfacePoints.getPos(idn); + Real length = norm(dir); + dir = getNormalized(dir); + + // Decompose direction into normal and tangent directions. + Vec3 dn = dot(dir, gradient) * gradient; + Vec3 dt = dir - dn; + + Real w = weightSurfaceTangent(length); + wt += w; + tangentDisplacement += w * dt; + } + LOOP_NEIGHBORS_END + if (norm(tangentDisplacement) != 0) { + tangentDisplacement = getNormalized(tangentDisplacement); + } + + // check density criterion, add surface point if necessary + Vec3 creationPos = pos + params.meanFineDistance * tangentDisplacement; + if (isInDomain(creationPos) && + !surfacePoints.hasNeighbor(creationPos, params.meanFineDistance - (1e-6))) { + // create point + surfacePoints.addBuffered(creationPos); + } + } + + surfacePoints.doCompress(); + surfacePoints.insertBufferedParticles(); + + // check density criterion, delete surface points if necessary + fixedSize = surfacePoints.size(); + for (int idx = 0; idx < fixedSize; idx++) { + if (!isInDomain(surfacePoints.getPos(idx)) || + surfacePoints.hasNeighborOtherThanItself(idx, 0.67 * params.meanFineDistance)) { + surfacePoints.kill(idx); + } + } + + // delete surface points if no coarse neighbors in advection radius + fixedSize = surfacePoints.size(); + for (int idx = 0; idx < fixedSize; idx++) { + Vec3 pos = surfacePoints.getPos(idx); + if (!coarseParticles.hasNeighbor(pos, 2.f * params.outerRadius)) { + surfacePoints.kill(idx); + } + } + + // delete surface point if too far from constraint + fixedSize = surfacePoints.size(); + for (int idx = 0; idx < fixedSize; idx++) { + Real level = computeConstraintLevel(coarseParticles, surfacePoints.getPos(idx)); + if (level < -0.2 || level > 1.2) { + surfacePoints.kill(idx); + } + } + + surfacePoints.doCompress(); + surfacePoints.insertBufferedParticles(); +} + +// +// **** surface maintenance **** +// + +struct computeSurfaceDensities : public KernelBase { + computeSurfaceDensities(const BasicParticleSystemWrapper &surfacePoints, void *dummy) + : KernelBase(surfacePoints.size()), surfacePoints(surfacePoints), dummy(dummy) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, const BasicParticleSystemWrapper &surfacePoints, void *dummy) const + { + Vec3 pos = surfacePoints.getPos(idx); + Real density = 0; + LOOP_NEIGHBORS_BEGIN(surfacePoints, pos, params.normalRadius) + LOOP_GHOSTS_POS_BEGIN(surfacePoints.getPos(idn), params.normalRadius) + density += weightSurfaceNormal(norm(pos - gPos)); + LOOP_GHOSTS_END + LOOP_NEIGHBORS_END + tempSurfaceFloat[idx] = density; + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline void *getArg1() + { + return dummy; + } + typedef void type1; + void runMessage() + { + debMsg("Executing kernel computeSurfaceDensities ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, dummy); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + void *dummy; +}; + +struct computeSurfaceDisplacements : public KernelBase { + computeSurfaceDisplacements(const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceNormals(surfaceNormals) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals) const + { + Vec3 pos = surfacePoints.getPos(idx); + Vec3 normal = surfaceNormals[idx]; + + Vec3 displacementNormal(0, 0, 0); + Vec3 displacementTangent(0, 0, 0); + Real wTotal = 0; + LOOP_NEIGHBORS_BEGIN(surfacePoints, pos, params.normalRadius) + + LOOP_GHOSTS_POS_NORMAL_BEGIN( + surfacePoints.getPos(idn), surfaceNormals[idn], params.normalRadius) + Vec3 dir = pos - gPos; + Real length = norm(dir); + Vec3 dn = dot(dir, surfaceNormals[idx]) * surfaceNormals[idx]; + Vec3 dt = dir - dn; + if (tempSurfaceFloat[idn] == 0) { + continue; + } + Real w = weightSurfaceNormal(length) / tempSurfaceFloat[idn]; + + Vec3 crossVec = getNormalized(cross(normal, -dir)); + Vec3 projectedNormal = getNormalized(gNormal - dot(crossVec, gNormal) * crossVec); + if (dot(projectedNormal, normal) < 0 || abs(dot(normal, normal + projectedNormal)) < 1e-6) { + continue; + } + dn = -dot(normal + projectedNormal, dir) / dot(normal, normal + projectedNormal) * normal; + + displacementNormal += w * dn; + displacementTangent += w * getNormalized(dt); + wTotal += w; + LOOP_GHOSTS_END + + LOOP_NEIGHBORS_END + if (wTotal != 0) { + displacementNormal /= wTotal; + displacementTangent /= wTotal; + } + displacementNormal *= .75f; + displacementTangent *= .25f * params.meanFineDistance; + tempSurfaceVec3[idx] = displacementNormal + displacementTangent; + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const ParticleDataImpl<Vec3> &getArg1() + { + return surfaceNormals; + } + typedef ParticleDataImpl<Vec3> type1; + void runMessage() + { + debMsg("Executing kernel computeSurfaceDisplacements ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceNormals); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + const ParticleDataImpl<Vec3> &surfaceNormals; +}; + +struct applySurfaceDisplacements : public KernelBase { + applySurfaceDisplacements(BasicParticleSystemWrapper &surfacePoints, void *dummy) + : KernelBase(surfacePoints.size()), surfacePoints(surfacePoints), dummy(dummy) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, BasicParticleSystemWrapper &surfacePoints, void *dummy) const + { + surfacePoints.setPos(idx, surfacePoints.getPos(idx) + tempSurfaceVec3[idx]); + } + inline BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline void *getArg1() + { + return dummy; + } + typedef void type1; + void runMessage() + { + debMsg("Executing kernel applySurfaceDisplacements ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, dummy); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystemWrapper &surfacePoints; + void *dummy; +}; + +void regularizeSurfacePoints(BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals) +{ + tempSurfaceVec3.resize(surfacePoints.size()); + tempSurfaceFloat.resize(surfacePoints.size()); + + computeSurfaceDensities(surfacePoints, 0); + computeSurfaceDisplacements(surfacePoints, surfaceNormals); + applySurfaceDisplacements(surfacePoints, 0); +} + +struct constrainSurface : public KernelBase { + constrainSurface(BasicParticleSystemWrapper &surfacePoints, + const BasicParticleSystemWrapper &coarseParticles) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + coarseParticles(coarseParticles) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + BasicParticleSystemWrapper &surfacePoints, + const BasicParticleSystemWrapper &coarseParticles) const + { + Vec3 pos = surfacePoints.getPos(idx); + Real level = computeConstraintLevel(coarseParticles, surfacePoints.getPos(idx)); + if (level > 1) { + surfacePoints.setPos( + idx, + pos - (params.outerRadius - params.innerRadius) * (level - 1) * + computeConstraintGradient(coarseParticles, surfacePoints.getPos(idx))); + } + else if (level < 0) { + surfacePoints.setPos( + idx, + pos - (params.outerRadius - params.innerRadius) * level * + computeConstraintGradient(coarseParticles, surfacePoints.getPos(idx))); + } + } + inline BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const BasicParticleSystemWrapper &getArg1() + { + return coarseParticles; + } + typedef BasicParticleSystemWrapper type1; + void runMessage() + { + debMsg("Executing kernel constrainSurface ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, coarseParticles); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + BasicParticleSystemWrapper &surfacePoints; + const BasicParticleSystemWrapper &coarseParticles; +}; + +struct interpolateNewWaveData : public KernelBase { + interpolateNewWaveData(const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveH, + ParticleDataImpl<Real> &surfaceWaveDtH, + ParticleDataImpl<Real> &surfaceWaveSeed, + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceWaveH(surfaceWaveH), + surfaceWaveDtH(surfaceWaveDtH), + surfaceWaveSeed(surfaceWaveSeed), + surfaceWaveSeedAmplitude(surfaceWaveSeedAmplitude) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveH, + ParticleDataImpl<Real> &surfaceWaveDtH, + ParticleDataImpl<Real> &surfaceWaveSeed, + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude) const + { + if (surfacePoints.getStatus(idx) & ParticleBase::PNEW) { + Vec3 pos = surfacePoints.getPos(idx); + surfaceWaveH[idx] = 0; + surfaceWaveDtH[idx] = 0; + Real wTotal = 0; + LOOP_NEIGHBORS_BEGIN(surfacePoints, pos, params.tangentRadius) + if (!(surfacePoints.getStatus(idn) & ParticleBase::PNEW)) { + Real w = weightSurfaceTangent(norm(pos - surfacePoints.getPos(idn))); + surfaceWaveH[idx] += w * surfaceWaveH[idn]; + surfaceWaveDtH[idx] += w * surfaceWaveDtH[idn]; + surfaceWaveSeed[idx] += w * surfaceWaveSeed[idn]; + surfaceWaveSeedAmplitude[idx] += w * surfaceWaveSeedAmplitude[idn]; + wTotal += w; + } + LOOP_NEIGHBORS_END + if (wTotal != 0) { + surfaceWaveH[idx] /= wTotal; + surfaceWaveDtH[idx] /= wTotal; + surfaceWaveSeed[idx] /= wTotal; + surfaceWaveSeedAmplitude[idx] /= wTotal; + } + } + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline ParticleDataImpl<Real> &getArg1() + { + return surfaceWaveH; + } + typedef ParticleDataImpl<Real> type1; + inline ParticleDataImpl<Real> &getArg2() + { + return surfaceWaveDtH; + } + typedef ParticleDataImpl<Real> type2; + inline ParticleDataImpl<Real> &getArg3() + { + return surfaceWaveSeed; + } + typedef ParticleDataImpl<Real> type3; + inline ParticleDataImpl<Real> &getArg4() + { + return surfaceWaveSeedAmplitude; + } + typedef ParticleDataImpl<Real> type4; + void runMessage() + { + debMsg("Executing kernel interpolateNewWaveData ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, + surfacePoints, + surfaceWaveH, + surfaceWaveDtH, + surfaceWaveSeed, + surfaceWaveSeedAmplitude); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + ParticleDataImpl<Real> &surfaceWaveH; + ParticleDataImpl<Real> &surfaceWaveDtH; + ParticleDataImpl<Real> &surfaceWaveSeed; + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude; +}; + +void surfaceMaintenance(const BasicParticleSystemWrapper &coarseParticles, + BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Vec3> &surfaceNormals, + ParticleDataImpl<Real> &surfaceWaveH, + ParticleDataImpl<Real> &surfaceWaveDtH, + ParticleDataImpl<Real> &surfaceWaveSeed, + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude, + int nbIterations) +{ + int countIterations = nbIterations; + while (countIterations > 0) { + addDeleteSurfacePoints(surfacePoints); + surfacePoints.updateAccel(); + computeSurfaceNormals(surfacePoints, coarseParticles, surfaceNormals); + smoothSurfaceNormals(surfacePoints, surfaceNormals); + + regularizeSurfacePoints(surfacePoints, surfaceNormals); + surfacePoints.updateAccel(); + constrainSurface(surfacePoints, coarseParticles); + surfacePoints.updateAccel(); + + interpolateNewWaveData( + surfacePoints, surfaceWaveH, surfaceWaveDtH, surfaceWaveSeed, surfaceWaveSeedAmplitude); + + countIterations--; + } +} + +// +// **** surface wave seeding and evolution **** +// + +struct addSeed : public KernelBase { + addSeed(const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveH, + const ParticleDataImpl<Real> &surfaceWaveSeed) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceWaveH(surfaceWaveH), + surfaceWaveSeed(surfaceWaveSeed) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveH, + const ParticleDataImpl<Real> &surfaceWaveSeed) const + { + surfaceWaveH[idx] += surfaceWaveSeed[idx]; + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline ParticleDataImpl<Real> &getArg1() + { + return surfaceWaveH; + } + typedef ParticleDataImpl<Real> type1; + inline const ParticleDataImpl<Real> &getArg2() + { + return surfaceWaveSeed; + } + typedef ParticleDataImpl<Real> type2; + void runMessage() + { + debMsg("Executing kernel addSeed ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceWaveH, surfaceWaveSeed); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + ParticleDataImpl<Real> &surfaceWaveH; + const ParticleDataImpl<Real> &surfaceWaveSeed; +}; + +struct computeSurfaceWaveNormal : public KernelBase { + computeSurfaceWaveNormal(const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals, + const ParticleDataImpl<Real> &surfaceWaveH) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceNormals(surfaceNormals), + surfaceWaveH(surfaceWaveH) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals, + const ParticleDataImpl<Real> &surfaceWaveH) const + { + Vec3 pos = surfacePoints.getPos(idx); + + // get tangent frame + Vec3 n = getNormalized(surfaceNormals[idx]); + Vec3 vx(1, 0, 0); + Vec3 vy(0, 1, 0); + Real dotX = dot(n, vx); + Real dotY = dot(n, vy); + Vec3 t1 = getNormalized(fabs(dotX) < fabs(dotY) ? cross(n, vx) : cross(n, vy)); + Vec3 t2 = getNormalized(cross(n, t1)); + + // linear fit + Real sw = 0, swx = 0, swy = 0, swxy = 0, swx2 = 0, swy2 = 0, swxz = 0, swyz = 0, swz = 0; + LOOP_NEIGHBORS_BEGIN(surfacePoints, pos, params.tangentRadius) + LOOP_GHOSTS_POS_BEGIN(surfacePoints.getPos(idn), params.tangentRadius) + Real x = dot(gPos - pos, t1); + Real y = dot(gPos - pos, t2); + Real z = surfaceWaveH[idn]; + Real w = weightSurfaceTangent(norm(pos - gPos)); + swx2 += w * x * x; + swy2 += w * y * y; + swxy += w * x * y; + swxz += w * x * z; + swyz += w * y * z; + swx += w * x; + swy += w * y; + swz += w * z; + sw += w; + LOOP_GHOSTS_END + LOOP_NEIGHBORS_END + Real det = -sw * swxy * swxy + 2.f * swx * swxy * swy - swx2 * swy * swy - swx * swx * swy2 + + sw * swx2 * swy2; + if (det == 0) { + tempSurfaceVec3[idx] = Vec3(0, 0, 0); + } + else { + Vec3 abc = 1.f / det * + Vec3(swxz * (-swy * swy + sw * swy2) + swyz * (-sw * swxy + swx * swy) + + swz * (swxy * swy - swx * swy2), + swxz * (-sw * swxy + swx * swy) + swyz * (-swx * swx + sw * swx2) + + swz * (swx * swxy - swx2 * swy), + swxz * (swxy * swy - swx * swy2) + swyz * (swx * swxy - swx2 * swy) + + swz * (-swxy * swxy + swx2 * swy2)); + Vec3 waveNormal = -getNormalized(vx * abc.x + vy * abc.y - Vec3(0, 0, 1)); + tempSurfaceVec3[idx] = waveNormal; + } + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const ParticleDataImpl<Vec3> &getArg1() + { + return surfaceNormals; + } + typedef ParticleDataImpl<Vec3> type1; + inline const ParticleDataImpl<Real> &getArg2() + { + return surfaceWaveH; + } + typedef ParticleDataImpl<Real> type2; + void runMessage() + { + debMsg("Executing kernel computeSurfaceWaveNormal ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceNormals, surfaceWaveH); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + const ParticleDataImpl<Vec3> &surfaceNormals; + const ParticleDataImpl<Real> &surfaceWaveH; +}; + +struct computeSurfaceWaveLaplacians : public KernelBase { + computeSurfaceWaveLaplacians(const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals, + const ParticleDataImpl<Real> &surfaceWaveH) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceNormals(surfaceNormals), + surfaceWaveH(surfaceWaveH) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals, + const ParticleDataImpl<Real> &surfaceWaveH) const + { + Real laplacian = 0; + Real wTotal = 0; + Vec3 pPos = surfacePoints.getPos(idx); + Vec3 pNormal = surfaceNormals[idx]; + + Vec3 vx(1, 0, 0); + Vec3 vy(0, 1, 0); + Real dotX = dot(pNormal, vx); + Real dotY = dot(pNormal, vy); + Vec3 t1 = getNormalized(fabs(dotX) < fabs(dotY) ? cross(pNormal, vx) : cross(pNormal, vy)); + Vec3 t2 = getNormalized(cross(pNormal, t1)); + + Vec3 pWaveNormal = tempSurfaceVec3[idx]; + Real ph = surfaceWaveH[idx]; + if (pWaveNormal.z == 0) { + tempSurfaceFloat[idx] = 0; + } + else { + + LOOP_NEIGHBORS_BEGIN(surfacePoints, pPos, params.tangentRadius) + Real nh = surfaceWaveH[idn]; + LOOP_GHOSTS_POS_BEGIN(surfacePoints.getPos(idn), params.tangentRadius) + Vec3 dir = gPos - pPos; + Real lengthDir = norm(dir); + if (lengthDir < 1e-5) + continue; + Vec3 tangentDir = lengthDir * getNormalized(dir - dot(dir, pNormal) * pNormal); + Real dirX = dot(tangentDir, t1); + Real dirY = dot(tangentDir, t2); + Real dz = nh - ph - (-pWaveNormal.x / pWaveNormal.z) * dirX - + (-pWaveNormal.y / pWaveNormal.z) * dirY; + Real w = weightSurfaceTangent(norm(pPos - gPos)); + wTotal += w; + laplacian += clamp(w * 4 * dz / (lengthDir * lengthDir), Real(-100.), Real(100.)); + LOOP_GHOSTS_END + LOOP_NEIGHBORS_END + if (wTotal != 0) { + tempSurfaceFloat[idx] = laplacian / wTotal; + } + else { + tempSurfaceFloat[idx] = 0; + } + } + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const ParticleDataImpl<Vec3> &getArg1() + { + return surfaceNormals; + } + typedef ParticleDataImpl<Vec3> type1; + inline const ParticleDataImpl<Real> &getArg2() + { + return surfaceWaveH; + } + typedef ParticleDataImpl<Real> type2; + void runMessage() + { + debMsg("Executing kernel computeSurfaceWaveLaplacians ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceNormals, surfaceWaveH); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + const ParticleDataImpl<Vec3> &surfaceNormals; + const ParticleDataImpl<Real> &surfaceWaveH; +}; + +struct evolveWave : public KernelBase { + evolveWave(const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveH, + ParticleDataImpl<Real> &surfaceWaveDtH, + const ParticleDataImpl<Real> &surfaceWaveSeed) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceWaveH(surfaceWaveH), + surfaceWaveDtH(surfaceWaveDtH), + surfaceWaveSeed(surfaceWaveSeed) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveH, + ParticleDataImpl<Real> &surfaceWaveDtH, + const ParticleDataImpl<Real> &surfaceWaveSeed) const + { + surfaceWaveDtH[idx] += params.waveSpeed * params.waveSpeed * params.dt * tempSurfaceFloat[idx]; + surfaceWaveDtH[idx] /= (1 + params.dt * params.waveDamping); + surfaceWaveH[idx] += params.dt * surfaceWaveDtH[idx]; + surfaceWaveH[idx] /= (1 + params.dt * params.waveDamping); + surfaceWaveH[idx] -= surfaceWaveSeed[idx]; + + // clamp H and DtH (to prevent rare extreme behaviors) + surfaceWaveDtH[idx] = clamp(surfaceWaveDtH[idx], + -params.waveMaxFrequency * params.waveMaxAmplitude, + params.waveMaxFrequency * params.waveMaxAmplitude); + surfaceWaveH[idx] = clamp( + surfaceWaveH[idx], -params.waveMaxAmplitude, params.waveMaxAmplitude); + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline ParticleDataImpl<Real> &getArg1() + { + return surfaceWaveH; + } + typedef ParticleDataImpl<Real> type1; + inline ParticleDataImpl<Real> &getArg2() + { + return surfaceWaveDtH; + } + typedef ParticleDataImpl<Real> type2; + inline const ParticleDataImpl<Real> &getArg3() + { + return surfaceWaveSeed; + } + typedef ParticleDataImpl<Real> type3; + void runMessage() + { + debMsg("Executing kernel evolveWave ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceWaveH, surfaceWaveDtH, surfaceWaveSeed); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + ParticleDataImpl<Real> &surfaceWaveH; + ParticleDataImpl<Real> &surfaceWaveDtH; + const ParticleDataImpl<Real> &surfaceWaveSeed; +}; + +struct computeSurfaceCurvature : public KernelBase { + computeSurfaceCurvature(const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceNormals(surfaceNormals) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals) const + { + Vec3 pPos = surfacePoints.getPos(idx); + Real wTotal = 0; + Real curv = 0; + Vec3 pNormal = surfaceNormals[idx]; + + LOOP_NEIGHBORS_BEGIN(surfacePoints, pPos, params.normalRadius) + LOOP_GHOSTS_POS_NORMAL_BEGIN( + surfacePoints.getPos(idn), surfaceNormals[idn], params.normalRadius) + Vec3 dir = pPos - gPos; + if (dot(pNormal, gNormal) < 0) { + continue; + } // backfacing + Real dist = norm(dir); + if (dist < params.normalRadius / 100.f) { + continue; + } + + Real distn = dot(dir, pNormal); + + Real w = weightSurfaceNormal(dist); + curv += w * distn; + wTotal += w; + LOOP_GHOSTS_END + LOOP_NEIGHBORS_END + if (wTotal != 0) { + curv /= wTotal; + } + tempSurfaceFloat[idx] = fabs(curv); + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline const ParticleDataImpl<Vec3> &getArg1() + { + return surfaceNormals; + } + typedef ParticleDataImpl<Vec3> type1; + void runMessage() + { + debMsg("Executing kernel computeSurfaceCurvature ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceNormals); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + const ParticleDataImpl<Vec3> &surfaceNormals; +}; + +struct smoothCurvature : public KernelBase { + smoothCurvature(const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveSource) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceWaveSource(surfaceWaveSource) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveSource) const + { + Vec3 pPos = surfacePoints.getPos(idx); + Real curv = 0; + Real wTotal = 0; + + LOOP_NEIGHBORS_BEGIN(surfacePoints, pPos, params.normalRadius) + Real w = weightSurfaceNormal(norm(pPos - surfacePoints.getPos(idn))); + curv += w * tempSurfaceFloat[idn]; + wTotal += w; + LOOP_NEIGHBORS_END + if (wTotal != 0) { + curv /= wTotal; + } + surfaceWaveSource[idx] = curv; + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline ParticleDataImpl<Real> &getArg1() + { + return surfaceWaveSource; + } + typedef ParticleDataImpl<Real> type1; + void runMessage() + { + debMsg("Executing kernel smoothCurvature ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceWaveSource); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + ParticleDataImpl<Real> &surfaceWaveSource; +}; + +struct seedWaves : public KernelBase { + seedWaves(const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveSeed, + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude, + ParticleDataImpl<Real> &surfaceWaveSource) + : KernelBase(surfacePoints.size()), + surfacePoints(surfacePoints), + surfaceWaveSeed(surfaceWaveSeed), + surfaceWaveSeedAmplitude(surfaceWaveSeedAmplitude), + surfaceWaveSource(surfaceWaveSource) + { + runMessage(); + run(); + } + inline void op(IndexInt idx, + const BasicParticleSystemWrapper &surfacePoints, + ParticleDataImpl<Real> &surfaceWaveSeed, + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude, + ParticleDataImpl<Real> &surfaceWaveSource) const + { + Real source = smoothstep(params.waveSeedingCurvatureThresholdRegionCenter - + params.waveSeedingCurvatureThresholdRegionRadius, + params.waveSeedingCurvatureThresholdRegionCenter + + params.waveSeedingCurvatureThresholdRegionRadius, + (Real)surfaceWaveSource[idx]) * + 2.f - + 1.f; + Real freq = params.waveSeedFrequency; + Real theta = params.dt * frameCount * params.waveSpeed * freq; + Real costheta = cosf(theta); + Real maxSeedAmplitude = params.waveMaxSeedingAmplitude * params.waveMaxAmplitude; + + surfaceWaveSeedAmplitude[idx] = clamp<Real>(surfaceWaveSeedAmplitude[idx] + + source * params.waveSeedStepSizeRatioOfMax * + maxSeedAmplitude, + 0.f, + maxSeedAmplitude); + surfaceWaveSeed[idx] = surfaceWaveSeedAmplitude[idx] * costheta; + + // source values for display (not used after this point anyway) + surfaceWaveSource[idx] = (source >= 0) ? 1 : 0; + } + inline const BasicParticleSystemWrapper &getArg0() + { + return surfacePoints; + } + typedef BasicParticleSystemWrapper type0; + inline ParticleDataImpl<Real> &getArg1() + { + return surfaceWaveSeed; + } + typedef ParticleDataImpl<Real> type1; + inline ParticleDataImpl<Real> &getArg2() + { + return surfaceWaveSeedAmplitude; + } + typedef ParticleDataImpl<Real> type2; + inline ParticleDataImpl<Real> &getArg3() + { + return surfaceWaveSource; + } + typedef ParticleDataImpl<Real> type3; + void runMessage() + { + debMsg("Executing kernel seedWaves ", 3); + debMsg("Kernel range" + << " size " << size << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) + op(idx, surfacePoints, surfaceWaveSeed, surfaceWaveSeedAmplitude, surfaceWaveSource); + } + void run() + { + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this); + } + const BasicParticleSystemWrapper &surfacePoints; + ParticleDataImpl<Real> &surfaceWaveSeed; + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude; + ParticleDataImpl<Real> &surfaceWaveSource; +}; + +void surfaceWaves(const BasicParticleSystemWrapper &surfacePoints, + const ParticleDataImpl<Vec3> &surfaceNormals, + ParticleDataImpl<Real> &surfaceWaveH, + ParticleDataImpl<Real> &surfaceWaveDtH, + ParticleDataImpl<Real> &surfaceWaveSource, + ParticleDataImpl<Real> &surfaceWaveSeed, + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude) +{ + addSeed(surfacePoints, surfaceWaveH, surfaceWaveSeed); + computeSurfaceWaveNormal(surfacePoints, surfaceNormals, surfaceWaveH); + computeSurfaceWaveLaplacians(surfacePoints, surfaceNormals, surfaceWaveH); + evolveWave(surfacePoints, surfaceWaveH, surfaceWaveDtH, surfaceWaveSeed); + computeSurfaceCurvature(surfacePoints, surfaceNormals); + smoothCurvature(surfacePoints, surfaceWaveSource); + seedWaves(surfacePoints, surfaceWaveSeed, surfaceWaveSeedAmplitude, surfaceWaveSource); +} + +// +// **** main function **** +// + +void particleSurfaceTurbulence(const FlagGrid &flags, + BasicParticleSystem &coarseParts, + ParticleDataImpl<Vec3> &coarsePartsPrevPos, + BasicParticleSystem &surfPoints, + ParticleDataImpl<Vec3> &surfaceNormals, + ParticleDataImpl<Real> &surfaceWaveH, + ParticleDataImpl<Real> &surfaceWaveDtH, + BasicParticleSystem &surfacePointsDisplaced, + ParticleDataImpl<Real> &surfaceWaveSource, + ParticleDataImpl<Real> &surfaceWaveSeed, + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude, + int res, + Real outerRadius = 1.0f, + int surfaceDensity = 20, + int nbSurfaceMaintenanceIterations = 4, + Real dt = 0.005f, + Real waveSpeed = 16.0f, + Real waveDamping = 0.0f, + Real waveSeedFrequency = 4, + Real waveMaxAmplitude = 0.25f, + Real waveMaxFrequency = 800, + Real waveMaxSeedingAmplitude = 0.5, + Real waveSeedingCurvatureThresholdRegionCenter = 0.025f, + Real waveSeedingCurvatureThresholdRegionRadius = 0.01f, + Real waveSeedStepSizeRatioOfMax = 0.05f) +{ +#if USE_CHRONO == 1 + static std::chrono::high_resolution_clock::time_point begin, end; + end = std::chrono::high_resolution_clock::now(); + cout << std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() / 1000000000.f + << " : time sim" << endl; + begin = std::chrono::high_resolution_clock::now(); +#endif + + // wrap data + coarseParticles.points = &coarseParts; + coarseParticlesPrevPos.points = &coarsePartsPrevPos; + surfacePoints.points = &surfPoints; + + // copy parameters + params.res = res; + params.outerRadius = outerRadius; + params.surfaceDensity = surfaceDensity; + params.nbSurfaceMaintenanceIterations = nbSurfaceMaintenanceIterations; + params.dt = dt; + params.waveSpeed = waveSpeed; + params.waveDamping = waveDamping; + params.waveSeedFrequency = waveSeedFrequency; + params.waveMaxAmplitude = waveMaxAmplitude; + params.waveMaxFrequency = waveMaxFrequency; + params.waveMaxSeedingAmplitude = waveMaxSeedingAmplitude; + params.waveSeedingCurvatureThresholdRegionCenter = waveSeedingCurvatureThresholdRegionCenter; + params.waveSeedingCurvatureThresholdRegionRadius = waveSeedingCurvatureThresholdRegionRadius; + params.waveSeedStepSizeRatioOfMax = waveSeedStepSizeRatioOfMax; + + // compute other parameters + params.innerRadius = params.outerRadius / 2.0; + params.meanFineDistance = M_PI * (params.outerRadius + params.innerRadius) / + params.surfaceDensity; + params.constraintA = logf(2.0f / (1.0f + weightKernelCoarseDensity(params.outerRadius + + params.innerRadius))) / + (powf((params.outerRadius + params.innerRadius) / 2, 2) - + params.innerRadius * params.innerRadius); + params.normalRadius = 0.5f * (params.outerRadius + params.innerRadius); + params.tangentRadius = 2.1f * params.meanFineDistance; + params.bndXm = params.bndYm = params.bndZm = 2; + params.bndXp = params.bndYp = params.bndZp = params.res - 2; + + if (frameCount == 0) { + + // initialize accel grids + accelCoarse.init(2.f * res / params.outerRadius); + accelSurface.init(1.f * res / (2.f * params.meanFineDistance)); + + // update coarse accel structure + coarseParticles.updateAccel(); + + // create surface points + initFines(coarseParticles, surfacePoints, flags); + + // smooth surface + surfaceMaintenance(coarseParticles, + surfacePoints, + surfaceNormals, + surfaceWaveH, + surfaceWaveDtH, + surfaceWaveSeed, + surfaceWaveSeedAmplitude, + 6 * params.nbSurfaceMaintenanceIterations); + + // set wave values to zero + for (int idx = 0; idx < surfacePoints.size(); idx++) { + surfaceWaveH[idx] = 0; + surfaceWaveDtH[idx] = 0; + surfaceWaveSeed[idx] = 0; + surfaceWaveSeedAmplitude[idx] = 0; + } + } + else { + + // update coarse accel structure with previous coarse particles positions + coarseParticlesPrevPos.updateAccel(); + + // advect surface points following coarse particles + advectSurfacePoints(surfacePoints, coarseParticles, coarseParticlesPrevPos); + surfacePoints.updateAccel(); + + // update acceleration structure for surface points + coarseParticles.updateAccel(); + + // surface maintenance + surfaceMaintenance(coarseParticles, + surfacePoints, + surfaceNormals, + surfaceWaveH, + surfaceWaveDtH, + surfaceWaveSeed, + surfaceWaveSeedAmplitude, + params.nbSurfaceMaintenanceIterations); + + // surface waves + surfaceWaves(surfacePoints, + surfaceNormals, + surfaceWaveH, + surfaceWaveDtH, + surfaceWaveSource, + surfaceWaveSeed, + surfaceWaveSeedAmplitude); + } + frameCount++; + + // save positions as previous positions for next step + for (int id = 0; id < coarseParticles.size(); id++) { + if ((coarseParticles.getStatus(id) & ParticleBase::PNEW) == 0 && + (coarseParticles.getStatus(id) & ParticleBase::PDELETE) == 0) { + coarseParticlesPrevPos.setVec3(id, coarseParticles.getPos(id)); + } + } + + // create displaced points for display + surfacePointsDisplaced.clear(); + for (int idx = 0; idx < surfacePoints.size(); idx++) { + if ((surfacePoints.getStatus(idx) & ParticleBase::PDELETE) == 0) { + surfacePointsDisplaced.addParticle(surfacePoints.getPos(idx) + + surfaceNormals[idx] * surfaceWaveH[idx]); + } + } + +#if USE_CHRONO == 1 + end = std::chrono::high_resolution_clock::now(); + cout << std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() / 1000000000.f + << " : time upres" << endl; + begin = std::chrono::high_resolution_clock::now(); +#endif +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "particleSurfaceTurbulence", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + BasicParticleSystem &coarseParts = *_args.getPtr<BasicParticleSystem>( + "coarseParts", 1, &_lock); + ParticleDataImpl<Vec3> &coarsePartsPrevPos = *_args.getPtr<ParticleDataImpl<Vec3>>( + "coarsePartsPrevPos", 2, &_lock); + BasicParticleSystem &surfPoints = *_args.getPtr<BasicParticleSystem>( + "surfPoints", 3, &_lock); + ParticleDataImpl<Vec3> &surfaceNormals = *_args.getPtr<ParticleDataImpl<Vec3>>( + "surfaceNormals", 4, &_lock); + ParticleDataImpl<Real> &surfaceWaveH = *_args.getPtr<ParticleDataImpl<Real>>( + "surfaceWaveH", 5, &_lock); + ParticleDataImpl<Real> &surfaceWaveDtH = *_args.getPtr<ParticleDataImpl<Real>>( + "surfaceWaveDtH", 6, &_lock); + BasicParticleSystem &surfacePointsDisplaced = *_args.getPtr<BasicParticleSystem>( + "surfacePointsDisplaced", 7, &_lock); + ParticleDataImpl<Real> &surfaceWaveSource = *_args.getPtr<ParticleDataImpl<Real>>( + "surfaceWaveSource", 8, &_lock); + ParticleDataImpl<Real> &surfaceWaveSeed = *_args.getPtr<ParticleDataImpl<Real>>( + "surfaceWaveSeed", 9, &_lock); + ParticleDataImpl<Real> &surfaceWaveSeedAmplitude = *_args.getPtr<ParticleDataImpl<Real>>( + "surfaceWaveSeedAmplitude", 10, &_lock); + int res = _args.get<int>("res", 11, &_lock); + Real outerRadius = _args.getOpt<Real>("outerRadius", 12, 1.0f, &_lock); + int surfaceDensity = _args.getOpt<int>("surfaceDensity", 13, 20, &_lock); + int nbSurfaceMaintenanceIterations = _args.getOpt<int>( + "nbSurfaceMaintenanceIterations", 14, 4, &_lock); + Real dt = _args.getOpt<Real>("dt", 15, 0.005f, &_lock); + Real waveSpeed = _args.getOpt<Real>("waveSpeed", 16, 16.0f, &_lock); + Real waveDamping = _args.getOpt<Real>("waveDamping", 17, 0.0f, &_lock); + Real waveSeedFrequency = _args.getOpt<Real>("waveSeedFrequency", 18, 4, &_lock); + Real waveMaxAmplitude = _args.getOpt<Real>("waveMaxAmplitude", 19, 0.25f, &_lock); + Real waveMaxFrequency = _args.getOpt<Real>("waveMaxFrequency", 20, 800, &_lock); + Real waveMaxSeedingAmplitude = _args.getOpt<Real>( + "waveMaxSeedingAmplitude", 21, 0.5, &_lock); + Real waveSeedingCurvatureThresholdRegionCenter = _args.getOpt<Real>( + "waveSeedingCurvatureThresholdRegionCenter", 22, 0.025f, &_lock); + Real waveSeedingCurvatureThresholdRegionRadius = _args.getOpt<Real>( + "waveSeedingCurvatureThresholdRegionRadius", 23, 0.01f, &_lock); + Real waveSeedStepSizeRatioOfMax = _args.getOpt<Real>( + "waveSeedStepSizeRatioOfMax", 24, 0.05f, &_lock); + _retval = getPyNone(); + particleSurfaceTurbulence(flags, + coarseParts, + coarsePartsPrevPos, + surfPoints, + surfaceNormals, + surfaceWaveH, + surfaceWaveDtH, + surfacePointsDisplaced, + surfaceWaveSource, + surfaceWaveSeed, + surfaceWaveSeedAmplitude, + res, + outerRadius, + surfaceDensity, + nbSurfaceMaintenanceIterations, + dt, + waveSpeed, + waveDamping, + waveSeedFrequency, + waveMaxAmplitude, + waveMaxFrequency, + waveMaxSeedingAmplitude, + waveSeedingCurvatureThresholdRegionCenter, + waveSeedingCurvatureThresholdRegionRadius, + waveSeedStepSizeRatioOfMax); + _args.check(); + } + pbFinalizePlugin(parent, "particleSurfaceTurbulence", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("particleSurfaceTurbulence", e.what()); + return 0; + } +} +static const Pb::Register _RP_particleSurfaceTurbulence("", "particleSurfaceTurbulence", _W_0); +extern "C" { +void PbRegister_particleSurfaceTurbulence() +{ + KEEP_UNUSED(_RP_particleSurfaceTurbulence); +} +} + +void debugCheckParts(const BasicParticleSystem &parts, const FlagGrid &flags) +{ + for (int idx = 0; idx < parts.size(); idx++) { + Vec3i p = toVec3i(parts.getPos(idx)); + if (!flags.isInBounds(p)) { + debMsg("bad position??? " << idx << " " << parts.getPos(idx), 1); + exit(1); + } + } +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "debugCheckParts", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const BasicParticleSystem &parts = *_args.getPtr<BasicParticleSystem>("parts", 0, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock); + _retval = getPyNone(); + debugCheckParts(parts, flags); + _args.check(); + } + pbFinalizePlugin(parent, "debugCheckParts", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("debugCheckParts", e.what()); + return 0; + } +} +static const Pb::Register _RP_debugCheckParts("", "debugCheckParts", _W_1); +extern "C" { +void PbRegister_debugCheckParts() +{ + KEEP_UNUSED(_RP_debugCheckParts); +} +} + +} // namespace SurfaceTurbulence + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/vortexplugins.cpp b/extern/mantaflow/preprocessed/plugin/vortexplugins.cpp new file mode 100644 index 00000000000..c2a21d82689 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/vortexplugins.cpp @@ -0,0 +1,695 @@ + + +// 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 <iostream> +#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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "markAsFixed", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Mesh &mesh = *_args.getPtr<Mesh>("mesh", 0, &_lock); + const Shape *shape = _args.getPtr<Shape>("shape", 1, &_lock); + bool exclusive = _args.getOpt<bool>("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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "texcoordInflow", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + VortexSheetMesh &mesh = *_args.getPtr<VortexSheetMesh>("mesh", 0, &_lock); + const Shape *shape = _args.getPtr<Shape>("shape", 1, &_lock); + const MACGrid &vel = *_args.getPtr<MACGrid>("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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "meshSmokeInflow", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + VortexSheetMesh &mesh = *_args.getPtr<VortexSheetMesh>("mesh", 0, &_lock); + const Shape *shape = _args.getPtr<Shape>("shape", 1, &_lock); + Real amount = _args.get<Real>("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<IndexInt> &__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<IndexInt>(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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "vorticitySource", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + VortexSheetMesh &mesh = *_args.getPtr<VortexSheetMesh>("mesh", 0, &_lock); + Vec3 gravity = _args.get<Vec3>("gravity", 1, &_lock); + const MACGrid *vel = _args.getPtrOpt<MACGrid>("vel", 2, NULL, &_lock); + const MACGrid *velOld = _args.getPtrOpt<MACGrid>("velOld", 3, NULL, &_lock); + Real scale = _args.getOpt<Real>("scale", 4, 0.1, &_lock); + Real maxAmount = _args.getOpt<Real>("maxAmount", 5, 0, &_lock); + Real mult = _args.getOpt<Real>("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<Vec3> vort(mesh.numTris()), pos(mesh.numTris()); + vector<Real> weights(3 * mesh.numTris()); + vector<int> 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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "smoothVorticity", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + VortexSheetMesh &mesh = *_args.getPtr<VortexSheetMesh>("mesh", 0, &_lock); + int iter = _args.getOpt<int>("iter", 1, 1, &_lock); + Real sigma = _args.getOpt<Real>("sigma", 2, 0.2, &_lock); + Real alpha = _args.getOpt<Real>("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<Real> 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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "VPseedK41", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + VortexParticleSystem &system = *_args.getPtr<VortexParticleSystem>("system", 0, &_lock); + const Shape *shape = _args.getPtr<Shape>("shape", 1, &_lock); + Real strength = _args.getOpt<Real>("strength", 2, 0, &_lock); + Real sigma0 = _args.getOpt<Real>("sigma0", 3, 0.2, &_lock); + Real sigma1 = _args.getOpt<Real>("sigma1", 4, 1.0, &_lock); + Real probability = _args.getOpt<Real>("probability", 5, 1.0, &_lock); + Real N = _args.getOpt<Real>("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<Vec3> &vel, + const FlagGrid &flags, + Grid<Vec3> *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<Vec3> vortTemp(mesh.getParent()); + Grid<Vec3> &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<Vec3> vortexCurl(mesh.getParent()); + Grid<Real> rhs(mesh.getParent()); + Grid<Real> solution(mesh.getParent()); + Grid<Real> residual(mesh.getParent()); + Grid<Real> search(mesh.getParent()); + Grid<Real> temp1(mesh.getParent()); + Grid<Real> A0(mesh.getParent()); + Grid<Real> Ai(mesh.getParent()); + Grid<Real> Aj(mesh.getParent()); + Grid<Real> Ak(mesh.getParent()); + Grid<Real> pca0(mesh.getParent()); + Grid<Real> pca1(mesh.getParent()); + Grid<Real> pca2(mesh.getParent()); + Grid<Real> 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<ApplyMatrix>( + 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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "VICintegration", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + VortexSheetMesh &mesh = *_args.getPtr<VortexSheetMesh>("mesh", 0, &_lock); + Real sigma = _args.get<Real>("sigma", 1, &_lock); + Grid<Vec3> &vel = *_args.getPtr<Grid<Vec3>>("vel", 2, &_lock); + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 3, &_lock); + Grid<Vec3> *vorticity = _args.getPtrOpt<Grid<Vec3>>("vorticity", 4, NULL, &_lock); + Real cgMaxIterFac = _args.getOpt<Real>("cgMaxIterFac", 5, 1.5, &_lock); + Real cgAccuracy = _args.getOpt<Real>("cgAccuracy", 6, 1e-3, &_lock); + Real scale = _args.getOpt<Real>("scale", 7, 0.01, &_lock); + int precondition = _args.getOpt<int>("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<Real> &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<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "densityFromLevelset", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const LevelsetGrid &phi = *_args.getPtr<LevelsetGrid>("phi", 0, &_lock); + Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock); + Real value = _args.getOpt<Real>("value", 2, 1.0, &_lock); + Real sigma = _args.getOpt<Real>("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 diff --git a/extern/mantaflow/preprocessed/plugin/waveletturbulence.cpp b/extern/mantaflow/preprocessed/plugin/waveletturbulence.cpp new file mode 100644 index 00000000000..9d3bdaa3f21 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/waveletturbulence.cpp @@ -0,0 +1,1292 @@ + + +// 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 + * + * Functions for calculating wavelet turbulence, + * plus helpers to compute vorticity, and strain rate magnitude + * + ******************************************************************************/ + +#include "vectorbase.h" +#include "shapes.h" +#include "commonkernels.h" +#include "noisefield.h" + +using namespace std; + +namespace Manta { + +//***************************************************************************** + +// first some fairly generic interpolation functions for grids with multiple sizes + +//! same as in grid.h , but takes an additional optional "desired" size +inline void calcGridSizeFactorMod( + Vec3i s1, Vec3i s2, Vec3i optSize, Vec3 scale, Vec3 &sourceFactor, Vec3 &retOff) +{ + for (int c = 0; c < 3; c++) { + if (optSize[c] > 0) { + s2[c] = optSize[c]; + } + } + sourceFactor = calcGridSizeFactor(s1, s2) / scale; + retOff = -retOff * sourceFactor + sourceFactor * 0.5; +} + +void interpolateGrid(Grid<Real> &target, + const Grid<Real> &source, + Vec3 scale = Vec3(1.), + Vec3 offset = Vec3(0.), + Vec3i size = Vec3i(-1, -1, -1), + int orderSpace = 1) +{ + Vec3 sourceFactor(1.), off2 = offset; + calcGridSizeFactorMod(source.getSize(), target.getSize(), size, scale, sourceFactor, off2); + + // a brief note on a mantaflow specialty: the target grid has to be the first argument here! + // the parent fluidsolver object is taken from the first grid, and it determines the size of the + // loop for the kernel call. as we're writing into target, it's important to loop exactly over + // all cells of the target grid... (note, when calling the plugin in python, it doesnt matter + // anymore). + + // sourceFactor offset necessary to shift eval points by half a small cell width + knInterpolateGridTempl<Real>(target, source, sourceFactor, off2, orderSpace); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "interpolateGrid", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &target = *_args.getPtr<Grid<Real>>("target", 0, &_lock); + const Grid<Real> &source = *_args.getPtr<Grid<Real>>("source", 1, &_lock); + Vec3 scale = _args.getOpt<Vec3>("scale", 2, Vec3(1.), &_lock); + Vec3 offset = _args.getOpt<Vec3>("offset", 3, Vec3(0.), &_lock); + Vec3i size = _args.getOpt<Vec3i>("size", 4, Vec3i(-1, -1, -1), &_lock); + int orderSpace = _args.getOpt<int>("orderSpace", 5, 1, &_lock); + _retval = getPyNone(); + interpolateGrid(target, source, scale, offset, size, orderSpace); + _args.check(); + } + pbFinalizePlugin(parent, "interpolateGrid", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("interpolateGrid", e.what()); + return 0; + } +} +static const Pb::Register _RP_interpolateGrid("", "interpolateGrid", _W_0); +extern "C" { +void PbRegister_interpolateGrid() +{ + KEEP_UNUSED(_RP_interpolateGrid); +} +} + +void interpolateGridVec3(Grid<Vec3> &target, + const Grid<Vec3> &source, + Vec3 scale = Vec3(1.), + Vec3 offset = Vec3(0.), + Vec3i size = Vec3i(-1, -1, -1), + int orderSpace = 1) +{ + Vec3 sourceFactor(1.), off2 = offset; + calcGridSizeFactorMod(source.getSize(), target.getSize(), size, scale, sourceFactor, off2); + knInterpolateGridTempl<Vec3>(target, source, sourceFactor, off2, orderSpace); +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "interpolateGridVec3", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Vec3> &target = *_args.getPtr<Grid<Vec3>>("target", 0, &_lock); + const Grid<Vec3> &source = *_args.getPtr<Grid<Vec3>>("source", 1, &_lock); + Vec3 scale = _args.getOpt<Vec3>("scale", 2, Vec3(1.), &_lock); + Vec3 offset = _args.getOpt<Vec3>("offset", 3, Vec3(0.), &_lock); + Vec3i size = _args.getOpt<Vec3i>("size", 4, Vec3i(-1, -1, -1), &_lock); + int orderSpace = _args.getOpt<int>("orderSpace", 5, 1, &_lock); + _retval = getPyNone(); + interpolateGridVec3(target, source, scale, offset, size, orderSpace); + _args.check(); + } + pbFinalizePlugin(parent, "interpolateGridVec3", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("interpolateGridVec3", e.what()); + return 0; + } +} +static const Pb::Register _RP_interpolateGridVec3("", "interpolateGridVec3", _W_1); +extern "C" { +void PbRegister_interpolateGridVec3() +{ + KEEP_UNUSED(_RP_interpolateGridVec3); +} +} + +//! interpolate a mac velocity grid from one size to another size + +struct KnInterpolateMACGrid : public KernelBase { + KnInterpolateMACGrid(MACGrid &target, + const MACGrid &source, + const Vec3 &sourceFactor, + const Vec3 &off, + int orderSpace) + : KernelBase(&target, 0), + target(target), + source(source), + sourceFactor(sourceFactor), + off(off), + orderSpace(orderSpace) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + MACGrid &target, + const MACGrid &source, + const Vec3 &sourceFactor, + const Vec3 &off, + int orderSpace) const + { + Vec3 pos = Vec3(i, j, k) * sourceFactor + off; + + Real vx = source.getInterpolatedHi(pos - Vec3(0.5, 0, 0), orderSpace)[0]; + Real vy = source.getInterpolatedHi(pos - Vec3(0, 0.5, 0), orderSpace)[1]; + Real vz = 0.f; + if (source.is3D()) + vz = source.getInterpolatedHi(pos - Vec3(0, 0, 0.5), orderSpace)[2]; + + target(i, j, k) = Vec3(vx, vy, vz); + } + inline MACGrid &getArg0() + { + return target; + } + typedef MACGrid type0; + inline const MACGrid &getArg1() + { + return source; + } + typedef MACGrid type1; + inline const Vec3 &getArg2() + { + return sourceFactor; + } + typedef Vec3 type2; + inline const Vec3 &getArg3() + { + return off; + } + typedef Vec3 type3; + inline int &getArg4() + { + return orderSpace; + } + typedef int type4; + void runMessage() + { + debMsg("Executing kernel KnInterpolateMACGrid ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, target, source, sourceFactor, off, orderSpace); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, target, source, sourceFactor, off, orderSpace); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + MACGrid ⌖ + const MACGrid &source; + const Vec3 &sourceFactor; + const Vec3 &off; + int orderSpace; +}; + +void interpolateMACGrid(MACGrid &target, + const MACGrid &source, + Vec3 scale = Vec3(1.), + Vec3 offset = Vec3(0.), + Vec3i size = Vec3i(-1, -1, -1), + int orderSpace = 1) +{ + Vec3 sourceFactor(1.), off2 = offset; + calcGridSizeFactorMod(source.getSize(), target.getSize(), size, scale, sourceFactor, off2); + KnInterpolateMACGrid(target, source, sourceFactor, off2, orderSpace); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "interpolateMACGrid", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + MACGrid &target = *_args.getPtr<MACGrid>("target", 0, &_lock); + const MACGrid &source = *_args.getPtr<MACGrid>("source", 1, &_lock); + Vec3 scale = _args.getOpt<Vec3>("scale", 2, Vec3(1.), &_lock); + Vec3 offset = _args.getOpt<Vec3>("offset", 3, Vec3(0.), &_lock); + Vec3i size = _args.getOpt<Vec3i>("size", 4, Vec3i(-1, -1, -1), &_lock); + int orderSpace = _args.getOpt<int>("orderSpace", 5, 1, &_lock); + _retval = getPyNone(); + interpolateMACGrid(target, source, scale, offset, size, orderSpace); + _args.check(); + } + pbFinalizePlugin(parent, "interpolateMACGrid", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("interpolateMACGrid", e.what()); + return 0; + } +} +static const Pb::Register _RP_interpolateMACGrid("", "interpolateMACGrid", _W_2); +extern "C" { +void PbRegister_interpolateMACGrid() +{ + KEEP_UNUSED(_RP_interpolateMACGrid); +} +} + +//***************************************************************************** + +//! Apply vector noise to grid, this is a simplified version - no position scaling or UVs + +struct knApplySimpleNoiseVec3 : public KernelBase { + knApplySimpleNoiseVec3(const FlagGrid &flags, + Grid<Vec3> &target, + const WaveletNoiseField &noise, + Real scale, + const Grid<Real> *weight) + : KernelBase(&flags, 0), + flags(flags), + target(target), + noise(noise), + scale(scale), + weight(weight) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Vec3> &target, + const WaveletNoiseField &noise, + Real scale, + const Grid<Real> *weight) const + { + if (!flags.isFluid(i, j, k)) + return; + Real factor = 1; + if (weight) + factor = (*weight)(i, j, k); + target(i, j, k) += noise.evaluateCurl(Vec3(i, j, k) + Vec3(0.5)) * scale * factor; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Vec3> &getArg1() + { + return target; + } + typedef Grid<Vec3> type1; + inline const WaveletNoiseField &getArg2() + { + return noise; + } + typedef WaveletNoiseField type2; + inline Real &getArg3() + { + return scale; + } + typedef Real type3; + inline const Grid<Real> *getArg4() + { + return weight; + } + typedef Grid<Real> type4; + void runMessage() + { + debMsg("Executing kernel knApplySimpleNoiseVec3 ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, target, noise, scale, weight); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, target, noise, scale, weight); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Vec3> ⌖ + const WaveletNoiseField &noise; + Real scale; + const Grid<Real> *weight; +}; + +void applySimpleNoiseVec3(const FlagGrid &flags, + Grid<Vec3> &target, + const WaveletNoiseField &noise, + Real scale = 1.0, + const Grid<Real> *weight = NULL) +{ + // note - passing a MAC grid here is slightly inaccurate, we should evaluate each component + // separately + knApplySimpleNoiseVec3(flags, target, noise, scale, weight); +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "applySimpleNoiseVec3", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Vec3> &target = *_args.getPtr<Grid<Vec3>>("target", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Real scale = _args.getOpt<Real>("scale", 3, 1.0, &_lock); + const Grid<Real> *weight = _args.getPtrOpt<Grid<Real>>("weight", 4, NULL, &_lock); + _retval = getPyNone(); + applySimpleNoiseVec3(flags, target, noise, scale, weight); + _args.check(); + } + pbFinalizePlugin(parent, "applySimpleNoiseVec3", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("applySimpleNoiseVec3", e.what()); + return 0; + } +} +static const Pb::Register _RP_applySimpleNoiseVec3("", "applySimpleNoiseVec3", _W_3); +extern "C" { +void PbRegister_applySimpleNoiseVec3() +{ + KEEP_UNUSED(_RP_applySimpleNoiseVec3); +} +} + +//! Simple noise for a real grid , follows applySimpleNoiseVec3 + +struct knApplySimpleNoiseReal : public KernelBase { + knApplySimpleNoiseReal(const FlagGrid &flags, + Grid<Real> &target, + const WaveletNoiseField &noise, + Real scale, + const Grid<Real> *weight) + : KernelBase(&flags, 0), + flags(flags), + target(target), + noise(noise), + scale(scale), + weight(weight) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &target, + const WaveletNoiseField &noise, + Real scale, + const Grid<Real> *weight) const + { + if (!flags.isFluid(i, j, k)) + return; + Real factor = 1; + if (weight) + factor = (*weight)(i, j, k); + target(i, j, k) += noise.evaluate(Vec3(i, j, k) + Vec3(0.5)) * scale * factor; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return target; + } + typedef Grid<Real> type1; + inline const WaveletNoiseField &getArg2() + { + return noise; + } + typedef WaveletNoiseField type2; + inline Real &getArg3() + { + return scale; + } + typedef Real type3; + inline const Grid<Real> *getArg4() + { + return weight; + } + typedef Grid<Real> type4; + void runMessage() + { + debMsg("Executing kernel knApplySimpleNoiseReal ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, target, noise, scale, weight); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, target, noise, scale, weight); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> ⌖ + const WaveletNoiseField &noise; + Real scale; + const Grid<Real> *weight; +}; + +void applySimpleNoiseReal(const FlagGrid &flags, + Grid<Real> &target, + const WaveletNoiseField &noise, + Real scale = 1.0, + const Grid<Real> *weight = NULL) +{ + knApplySimpleNoiseReal(flags, target, noise, scale, weight); +} +static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "applySimpleNoiseReal", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &target = *_args.getPtr<Grid<Real>>("target", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Real scale = _args.getOpt<Real>("scale", 3, 1.0, &_lock); + const Grid<Real> *weight = _args.getPtrOpt<Grid<Real>>("weight", 4, NULL, &_lock); + _retval = getPyNone(); + applySimpleNoiseReal(flags, target, noise, scale, weight); + _args.check(); + } + pbFinalizePlugin(parent, "applySimpleNoiseReal", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("applySimpleNoiseReal", e.what()); + return 0; + } +} +static const Pb::Register _RP_applySimpleNoiseReal("", "applySimpleNoiseReal", _W_4); +extern "C" { +void PbRegister_applySimpleNoiseReal() +{ + KEEP_UNUSED(_RP_applySimpleNoiseReal); +} +} + +//! Apply vector-based wavelet noise to target grid +//! This is the version with more functionality - supports uv grids, and on-the-fly interpolation +//! of input grids. + +struct knApplyNoiseVec3 : public KernelBase { + knApplyNoiseVec3(const FlagGrid &flags, + Grid<Vec3> &target, + const WaveletNoiseField &noise, + Real scale, + Real scaleSpatial, + const Grid<Real> *weight, + const Grid<Vec3> *uv, + bool uvInterpol, + const Vec3 &sourceFactor) + : KernelBase(&flags, 0), + flags(flags), + target(target), + noise(noise), + scale(scale), + scaleSpatial(scaleSpatial), + weight(weight), + uv(uv), + uvInterpol(uvInterpol), + sourceFactor(sourceFactor) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Vec3> &target, + const WaveletNoiseField &noise, + Real scale, + Real scaleSpatial, + const Grid<Real> *weight, + const Grid<Vec3> *uv, + bool uvInterpol, + const Vec3 &sourceFactor) const + { + if (!flags.isFluid(i, j, k)) + return; + + // get weighting, interpolate if necessary + Real w = 1; + if (weight) { + if (!uvInterpol) { + w = (*weight)(i, j, k); + } + else { + w = weight->getInterpolated(Vec3(i, j, k) * sourceFactor); + } + } + + // compute position where to evaluate the noise + Vec3 pos = Vec3(i, j, k) + Vec3(0.5); + if (uv) { + if (!uvInterpol) { + pos = (*uv)(i, j, k); + } + else { + pos = uv->getInterpolated(Vec3(i, j, k) * sourceFactor); + // uv coordinates are in local space - so we need to adjust the values of the positions + pos /= sourceFactor; + } + } + pos *= scaleSpatial; + + Vec3 noiseVec3 = noise.evaluateCurl(pos) * scale * w; + // noiseVec3=pos; // debug , show interpolated positions + target(i, j, k) += noiseVec3; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Vec3> &getArg1() + { + return target; + } + typedef Grid<Vec3> type1; + inline const WaveletNoiseField &getArg2() + { + return noise; + } + typedef WaveletNoiseField type2; + inline Real &getArg3() + { + return scale; + } + typedef Real type3; + inline Real &getArg4() + { + return scaleSpatial; + } + typedef Real type4; + inline const Grid<Real> *getArg5() + { + return weight; + } + typedef Grid<Real> type5; + inline const Grid<Vec3> *getArg6() + { + return uv; + } + typedef Grid<Vec3> type6; + inline bool &getArg7() + { + return uvInterpol; + } + typedef bool type7; + inline const Vec3 &getArg8() + { + return sourceFactor; + } + typedef Vec3 type8; + void runMessage() + { + debMsg("Executing kernel knApplyNoiseVec3 ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, + j, + k, + flags, + target, + noise, + scale, + scaleSpatial, + weight, + uv, + uvInterpol, + sourceFactor); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, + j, + k, + flags, + target, + noise, + scale, + scaleSpatial, + weight, + uv, + uvInterpol, + sourceFactor); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + Grid<Vec3> ⌖ + const WaveletNoiseField &noise; + Real scale; + Real scaleSpatial; + const Grid<Real> *weight; + const Grid<Vec3> *uv; + bool uvInterpol; + const Vec3 &sourceFactor; +}; + +void applyNoiseVec3(const FlagGrid &flags, + Grid<Vec3> &target, + const WaveletNoiseField &noise, + Real scale = 1.0, + Real scaleSpatial = 1.0, + const Grid<Real> *weight = NULL, + const Grid<Vec3> *uv = NULL) +{ + // check whether the uv grid has a different resolution + bool uvInterpol = false; + // and pre-compute conversion (only used if uvInterpol==true) + // used for both uv and weight grid... + Vec3 sourceFactor = Vec3(1.); + if (uv) { + uvInterpol = (target.getSize() != uv->getSize()); + sourceFactor = calcGridSizeFactor(uv->getSize(), target.getSize()); + } + else if (weight) { + uvInterpol = (target.getSize() != weight->getSize()); + sourceFactor = calcGridSizeFactor(weight->getSize(), target.getSize()); + } + if (uv && weight) + assertMsg(uv->getSize() == weight->getSize(), "UV and weight grid have to match!"); + + // note - passing a MAC grid here is slightly inaccurate, we should evaluate each component + // separately + knApplyNoiseVec3( + flags, target, noise, scale, scaleSpatial, weight, uv, uvInterpol, sourceFactor); +} +static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "applyNoiseVec3", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Vec3> &target = *_args.getPtr<Grid<Vec3>>("target", 1, &_lock); + const WaveletNoiseField &noise = *_args.getPtr<WaveletNoiseField>("noise", 2, &_lock); + Real scale = _args.getOpt<Real>("scale", 3, 1.0, &_lock); + Real scaleSpatial = _args.getOpt<Real>("scaleSpatial", 4, 1.0, &_lock); + const Grid<Real> *weight = _args.getPtrOpt<Grid<Real>>("weight", 5, NULL, &_lock); + const Grid<Vec3> *uv = _args.getPtrOpt<Grid<Vec3>>("uv", 6, NULL, &_lock); + _retval = getPyNone(); + applyNoiseVec3(flags, target, noise, scale, scaleSpatial, weight, uv); + _args.check(); + } + pbFinalizePlugin(parent, "applyNoiseVec3", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("applyNoiseVec3", e.what()); + return 0; + } +} +static const Pb::Register _RP_applyNoiseVec3("", "applyNoiseVec3", _W_5); +extern "C" { +void PbRegister_applyNoiseVec3() +{ + KEEP_UNUSED(_RP_applyNoiseVec3); +} +} + +//! Compute energy of a staggered velocity field (at cell center) + +struct KnApplyComputeEnergy : public KernelBase { + KnApplyComputeEnergy(const FlagGrid &flags, const MACGrid &vel, Grid<Real> &energy) + : KernelBase(&flags, 0), flags(flags), vel(vel), energy(energy) + { + runMessage(); + run(); + } + inline void op( + int i, int j, int k, const FlagGrid &flags, const MACGrid &vel, Grid<Real> &energy) const + { + Real e = 0.f; + if (flags.isFluid(i, j, k)) { + Vec3 v = vel.getCentered(i, j, k); + e = 0.5 * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + } + energy(i, j, k) = e; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline const MACGrid &getArg1() + { + return vel; + } + typedef MACGrid type1; + inline Grid<Real> &getArg2() + { + return energy; + } + typedef Grid<Real> type2; + void runMessage() + { + debMsg("Executing kernel KnApplyComputeEnergy ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, energy); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, vel, energy); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this); + } + const FlagGrid &flags; + const MACGrid &vel; + Grid<Real> &energy; +}; + +void computeEnergy(const FlagGrid &flags, const MACGrid &vel, Grid<Real> &energy) +{ + KnApplyComputeEnergy(flags, vel, energy); +} +static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "computeEnergy", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); + Grid<Real> &energy = *_args.getPtr<Grid<Real>>("energy", 2, &_lock); + _retval = getPyNone(); + computeEnergy(flags, vel, energy); + _args.check(); + } + pbFinalizePlugin(parent, "computeEnergy", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("computeEnergy", e.what()); + return 0; + } +} +static const Pb::Register _RP_computeEnergy("", "computeEnergy", _W_6); +extern "C" { +void PbRegister_computeEnergy() +{ + KEEP_UNUSED(_RP_computeEnergy); +} +} + +void computeWaveletCoeffs(Grid<Real> &input) +{ + Grid<Real> temp1(input.getParent()), temp2(input.getParent()); + WaveletNoiseField::computeCoefficients(input, temp1, temp2); +} +static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "computeWaveletCoeffs", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &input = *_args.getPtr<Grid<Real>>("input", 0, &_lock); + _retval = getPyNone(); + computeWaveletCoeffs(input); + _args.check(); + } + pbFinalizePlugin(parent, "computeWaveletCoeffs", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("computeWaveletCoeffs", e.what()); + return 0; + } +} +static const Pb::Register _RP_computeWaveletCoeffs("", "computeWaveletCoeffs", _W_7); +extern "C" { +void PbRegister_computeWaveletCoeffs() +{ + KEEP_UNUSED(_RP_computeWaveletCoeffs); +} +} + +// note - alomst the same as for vorticity confinement +void computeVorticity(const MACGrid &vel, Grid<Vec3> &vorticity, Grid<Real> *norm = NULL) +{ + Grid<Vec3> velCenter(vel.getParent()); + GetCentered(velCenter, vel); + CurlOp(velCenter, vorticity); + if (norm) + GridNorm(*norm, vorticity); +} +static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "computeVorticity", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + Grid<Vec3> &vorticity = *_args.getPtr<Grid<Vec3>>("vorticity", 1, &_lock); + Grid<Real> *norm = _args.getPtrOpt<Grid<Real>>("norm", 2, NULL, &_lock); + _retval = getPyNone(); + computeVorticity(vel, vorticity, norm); + _args.check(); + } + pbFinalizePlugin(parent, "computeVorticity", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("computeVorticity", e.what()); + return 0; + } +} +static const Pb::Register _RP_computeVorticity("", "computeVorticity", _W_8); +extern "C" { +void PbRegister_computeVorticity() +{ + KEEP_UNUSED(_RP_computeVorticity); +} +} + +// note - very similar to KnComputeProductionStrain, but for use as wavelet turb weighting + +struct KnComputeStrainRateMag : public KernelBase { + KnComputeStrainRateMag(const MACGrid &vel, const Grid<Vec3> &velCenter, Grid<Real> &prod) + : KernelBase(&vel, 1), vel(vel), velCenter(velCenter), prod(prod) + { + runMessage(); + run(); + } + inline void op( + int i, int j, int k, const MACGrid &vel, const Grid<Vec3> &velCenter, Grid<Real> &prod) const + { + // compute Sij = 1/2 * (dU_i/dx_j + dU_j/dx_i) + Vec3 diag = Vec3(vel(i + 1, j, k).x, vel(i, j + 1, k).y, 0.) - vel(i, j, k); + if (vel.is3D()) + diag[2] += vel(i, j, k + 1).z; + else + diag[2] = 0.; + + Vec3 ux = 0.5 * (velCenter(i + 1, j, k) - velCenter(i - 1, j, k)); + Vec3 uy = 0.5 * (velCenter(i, j + 1, k) - velCenter(i, j - 1, k)); + Vec3 uz; + if (vel.is3D()) + uz = 0.5 * (velCenter(i, j, k + 1) - velCenter(i, j, k - 1)); + + Real S12 = 0.5 * (ux.y + uy.x); + Real S13 = 0.5 * (ux.z + uz.x); + Real S23 = 0.5 * (uy.z + uz.y); + Real S2 = square(diag.x) + square(diag.y) + square(diag.z) + 2.0 * square(S12) + + 2.0 * square(S13) + 2.0 * square(S23); + prod(i, j, k) = S2; + } + inline const MACGrid &getArg0() + { + return vel; + } + typedef MACGrid type0; + inline const Grid<Vec3> &getArg1() + { + return velCenter; + } + typedef Grid<Vec3> type1; + inline Grid<Real> &getArg2() + { + return prod; + } + typedef Grid<Real> type2; + void runMessage() + { + debMsg("Executing kernel KnComputeStrainRateMag ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, velCenter, prod); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, vel, velCenter, prod); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const MACGrid &vel; + const Grid<Vec3> &velCenter; + Grid<Real> ∏ +}; +void computeStrainRateMag(const MACGrid &vel, Grid<Real> &mag) +{ + Grid<Vec3> velCenter(vel.getParent()); + GetCentered(velCenter, vel); + KnComputeStrainRateMag(vel, velCenter, mag); +} +static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "computeStrainRateMag", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + Grid<Real> &mag = *_args.getPtr<Grid<Real>>("mag", 1, &_lock); + _retval = getPyNone(); + computeStrainRateMag(vel, mag); + _args.check(); + } + pbFinalizePlugin(parent, "computeStrainRateMag", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("computeStrainRateMag", e.what()); + return 0; + } +} +static const Pb::Register _RP_computeStrainRateMag("", "computeStrainRateMag", _W_9); +extern "C" { +void PbRegister_computeStrainRateMag() +{ + KEEP_UNUSED(_RP_computeStrainRateMag); +} +} + +// extrapolate a real grid into a flagged region (based on initial flags) +// by default extrapolates from fluid to obstacle cells +template<class T> +void extrapolSimpleFlagsHelper(const FlagGrid &flags, + Grid<T> &val, + int distance = 4, + int flagFrom = FlagGrid::TypeFluid, + int flagTo = FlagGrid::TypeObstacle) +{ + Grid<int> tmp(flags.getParent()); + int dim = (flags.is3D() ? 3 : 2); + const Vec3i nb[6] = {Vec3i(1, 0, 0), + Vec3i(-1, 0, 0), + Vec3i(0, 1, 0), + Vec3i(0, -1, 0), + Vec3i(0, 0, 1), + Vec3i(0, 0, -1)}; + + // remove all fluid cells (set to 1) + tmp.clear(); + bool foundTarget = false; + FOR_IJK_BND(flags, 0) + { + if (flags(i, j, k) & flagFrom) + tmp(Vec3i(i, j, k)) = 1; + if (!foundTarget && (flags(i, j, k) & flagTo)) + foundTarget = true; + } + // optimization, skip extrapolation if we dont have any cells to extrapolate to + if (!foundTarget) { + debMsg("No target cells found, skipping extrapolation", 1); + return; + } + + // extrapolate for given distance + for (int d = 1; d < 1 + distance; ++d) { + + // TODO, parallelize + FOR_IJK_BND(flags, 1) + { + if (tmp(i, j, k) != 0) + continue; + if (!(flags(i, j, k) & flagTo)) + continue; + + // copy from initialized neighbors + Vec3i p(i, j, k); + int nbs = 0; + T avgVal = 0.; + for (int n = 0; n < 2 * dim; ++n) { + if (tmp(p + nb[n]) == d) { + avgVal += val(p + nb[n]); + nbs++; + } + } + + if (nbs > 0) { + tmp(p) = d + 1; + val(p) = avgVal / nbs; + } + } + + } // distance +} + +void extrapolateSimpleFlags(const FlagGrid &flags, + GridBase *val, + int distance = 4, + int flagFrom = FlagGrid::TypeFluid, + int flagTo = FlagGrid::TypeObstacle) +{ + if (val->getType() & GridBase::TypeReal) { + extrapolSimpleFlagsHelper<Real>(flags, *((Grid<Real> *)val), distance, flagFrom, flagTo); + } + else if (val->getType() & GridBase::TypeInt) { + extrapolSimpleFlagsHelper<int>(flags, *((Grid<int> *)val), distance, flagFrom, flagTo); + } + else if (val->getType() & GridBase::TypeVec3) { + extrapolSimpleFlagsHelper<Vec3>(flags, *((Grid<Vec3> *)val), distance, flagFrom, flagTo); + } + else + errMsg("extrapolateSimpleFlags: Grid Type is not supported (only int, Real, Vec3)"); +} +static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "extrapolateSimpleFlags", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + GridBase *val = _args.getPtr<GridBase>("val", 1, &_lock); + int distance = _args.getOpt<int>("distance", 2, 4, &_lock); + int flagFrom = _args.getOpt<int>("flagFrom", 3, FlagGrid::TypeFluid, &_lock); + int flagTo = _args.getOpt<int>("flagTo", 4, FlagGrid::TypeObstacle, &_lock); + _retval = getPyNone(); + extrapolateSimpleFlags(flags, val, distance, flagFrom, flagTo); + _args.check(); + } + pbFinalizePlugin(parent, "extrapolateSimpleFlags", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("extrapolateSimpleFlags", e.what()); + return 0; + } +} +static const Pb::Register _RP_extrapolateSimpleFlags("", "extrapolateSimpleFlags", _W_10); +extern "C" { +void PbRegister_extrapolateSimpleFlags() +{ + KEEP_UNUSED(_RP_extrapolateSimpleFlags); +} +} + +//! convert vel to a centered grid, then compute its curl +void getCurl(const MACGrid &vel, Grid<Real> &vort, int comp) +{ + Grid<Vec3> velCenter(vel.getParent()), curl(vel.getParent()); + + GetCentered(velCenter, vel); + CurlOp(velCenter, curl); + GetComponent(curl, vort, comp); +} +static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "getCurl", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock); + Grid<Real> &vort = *_args.getPtr<Grid<Real>>("vort", 1, &_lock); + int comp = _args.get<int>("comp", 2, &_lock); + _retval = getPyNone(); + getCurl(vel, vort, comp); + _args.check(); + } + pbFinalizePlugin(parent, "getCurl", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("getCurl", e.what()); + return 0; + } +} +static const Pb::Register _RP_getCurl("", "getCurl", _W_11); +extern "C" { +void PbRegister_getCurl() +{ + KEEP_UNUSED(_RP_getCurl); +} +} + +} // namespace Manta diff --git a/extern/mantaflow/preprocessed/plugin/waves.cpp b/extern/mantaflow/preprocessed/plugin/waves.cpp new file mode 100644 index 00000000000..7745dce4711 --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/waves.cpp @@ -0,0 +1,483 @@ + + +// 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 + * + * Wave equation + * + ******************************************************************************/ + +#include "levelset.h" +#include "commonkernels.h" +#include "particle.h" +#include "conjugategrad.h" +#include <cmath> + +using namespace std; + +namespace Manta { + +/****************************************************************************** + * + * explicit integration + * + ******************************************************************************/ + +struct knCalcSecDeriv2d : public KernelBase { + knCalcSecDeriv2d(const Grid<Real> &v, Grid<Real> &ret) : KernelBase(&v, 1), v(v), ret(ret) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, const Grid<Real> &v, Grid<Real> &ret) const + { + ret(i, j, k) = (-4. * v(i, j, k) + v(i - 1, j, k) + v(i + 1, j, k) + v(i, j - 1, k) + + v(i, j + 1, k)); + } + inline const Grid<Real> &getArg0() + { + return v; + } + typedef Grid<Real> type0; + inline Grid<Real> &getArg1() + { + return ret; + } + typedef Grid<Real> type1; + void runMessage() + { + debMsg("Executing kernel knCalcSecDeriv2d ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, v, ret); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, v, ret); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const Grid<Real> &v; + Grid<Real> &ret; +}; +; + +//! calculate a second derivative for the wave equation +void calcSecDeriv2d(const Grid<Real> &v, Grid<Real> &curv) +{ + knCalcSecDeriv2d(v, curv); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "calcSecDeriv2d", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const Grid<Real> &v = *_args.getPtr<Grid<Real>>("v", 0, &_lock); + Grid<Real> &curv = *_args.getPtr<Grid<Real>>("curv", 1, &_lock); + _retval = getPyNone(); + calcSecDeriv2d(v, curv); + _args.check(); + } + pbFinalizePlugin(parent, "calcSecDeriv2d", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("calcSecDeriv2d", e.what()); + return 0; + } +} +static const Pb::Register _RP_calcSecDeriv2d("", "calcSecDeriv2d", _W_0); +extern "C" { +void PbRegister_calcSecDeriv2d() +{ + KEEP_UNUSED(_RP_calcSecDeriv2d); +} +} + +// mass conservation + +struct knTotalSum : public KernelBase { + knTotalSum(Grid<Real> &h) : KernelBase(&h, 1), h(h), sum(0) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, Grid<Real> &h, double &sum) + { + sum += h(i, j, k); + } + inline operator double() + { + return sum; + } + inline double &getRet() + { + return sum; + } + inline Grid<Real> &getArg0() + { + return h; + } + typedef Grid<Real> type0; + void runMessage() + { + debMsg("Executing kernel knTotalSum ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, h, sum); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, h, sum); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_reduce(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_reduce(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + knTotalSum(knTotalSum &o, tbb::split) : KernelBase(o), h(o.h), sum(0) + { + } + void join(const knTotalSum &o) + { + sum += o.sum; + } + Grid<Real> &h; + double sum; +}; + +//! calculate the sum of all values in a grid (for wave equation solves) +Real totalSum(Grid<Real> &height) +{ + knTotalSum ts(height); + return ts.sum; +} +static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "totalSum", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &height = *_args.getPtr<Grid<Real>>("height", 0, &_lock); + _retval = toPy(totalSum(height)); + _args.check(); + } + pbFinalizePlugin(parent, "totalSum", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("totalSum", e.what()); + return 0; + } +} +static const Pb::Register _RP_totalSum("", "totalSum", _W_1); +extern "C" { +void PbRegister_totalSum() +{ + KEEP_UNUSED(_RP_totalSum); +} +} + +//! normalize all values in a grid (for wave equation solves) +void normalizeSumTo(Grid<Real> &height, Real target) +{ + knTotalSum ts(height); + Real factor = target / ts.sum; + height.multConst(factor); +} +static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "normalizeSumTo", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + Grid<Real> &height = *_args.getPtr<Grid<Real>>("height", 0, &_lock); + Real target = _args.get<Real>("target", 1, &_lock); + _retval = getPyNone(); + normalizeSumTo(height, target); + _args.check(); + } + pbFinalizePlugin(parent, "normalizeSumTo", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("normalizeSumTo", e.what()); + return 0; + } +} +static const Pb::Register _RP_normalizeSumTo("", "normalizeSumTo", _W_2); +extern "C" { +void PbRegister_normalizeSumTo() +{ + KEEP_UNUSED(_RP_normalizeSumTo); +} +} + +/****************************************************************************** + * + * implicit time integration + * + ******************************************************************************/ + +//! Kernel: Construct the right-hand side of the poisson equation + +struct MakeRhsWE : public KernelBase { + MakeRhsWE(const FlagGrid &flags, + Grid<Real> &rhs, + const Grid<Real> &ut, + const Grid<Real> &utm1, + Real s, + bool crankNic = false) + : KernelBase(&flags, 1), flags(flags), rhs(rhs), ut(ut), utm1(utm1), s(s), crankNic(crankNic) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid<Real> &rhs, + const Grid<Real> &ut, + const Grid<Real> &utm1, + Real s, + bool crankNic = false) const + { + rhs(i, j, k) = (2. * ut(i, j, k) - utm1(i, j, k)); + if (crankNic) { + rhs(i, j, k) += s * (-4. * ut(i, j, k) + 1. * ut(i - 1, j, k) + 1. * ut(i + 1, j, k) + + 1. * ut(i, j - 1, k) + 1. * ut(i, j + 1, k)); + } + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid<Real> &getArg1() + { + return rhs; + } + typedef Grid<Real> type1; + inline const Grid<Real> &getArg2() + { + return ut; + } + typedef Grid<Real> type2; + inline const Grid<Real> &getArg3() + { + return utm1; + } + typedef Grid<Real> type3; + inline Real &getArg4() + { + return s; + } + typedef Real type4; + inline bool &getArg5() + { + return crankNic; + } + typedef bool type5; + void runMessage() + { + debMsg("Executing kernel MakeRhsWE ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range<IndexInt> &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, rhs, ut, utm1, s, crankNic); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, rhs, ut, utm1, s, crankNic); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this); + } + const FlagGrid &flags; + Grid<Real> &rhs; + const Grid<Real> &ut; + const Grid<Real> &utm1; + Real s; + bool crankNic; +}; + +//! do a CG solve for the wave equation (note, out grid only there for debugging... could be +//! removed) + +void cgSolveWE(const FlagGrid &flags, + Grid<Real> &ut, + Grid<Real> &utm1, + Grid<Real> &out, + bool crankNic = false, + Real cSqr = 0.25, + Real cgMaxIterFac = 1.5, + Real cgAccuracy = 1e-5) +{ + // reserve temp grids + FluidSolver *parent = flags.getParent(); + Grid<Real> rhs(parent); + Grid<Real> residual(parent); + Grid<Real> search(parent); + Grid<Real> A0(parent); + Grid<Real> Ai(parent); + Grid<Real> Aj(parent); + Grid<Real> Ak(parent); + Grid<Real> tmp(parent); + // solution... + out.clear(); + + // setup matrix and boundaries + MakeLaplaceMatrix(flags, A0, Ai, Aj, Ak); + Real dt = parent->getDt(); + Real s = dt * dt * cSqr * 0.5; + FOR_IJK(flags) + { + Ai(i, j, k) *= s; + Aj(i, j, k) *= s; + Ak(i, j, k) *= s; + A0(i, j, k) *= s; + A0(i, j, k) += 1.; + } + + // compute divergence and init right hand side + rhs.clear(); + // h=dt + // rhs: = 2 ut - ut-1 + // A: (h2 c2/ dx)=s , (1+4s)uij + s ui-1j + ... + // Cr.Nic. + // rhs: cr nic = 2 ut - ut-1 + h^2c^2/2 b + // A: (h2 c2/2 dx)=s , (1+4s)uij + s ui-1j + ... + MakeRhsWE kernMakeRhs(flags, rhs, ut, utm1, s, crankNic); + + const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4); + GridCgInterface *gcg; + if (flags.is3D()) + gcg = new GridCg<ApplyMatrix>(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + else + gcg = new GridCg<ApplyMatrix2D>(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + + gcg->setAccuracy(cgAccuracy); + + // no preconditioning for now... + for (int iter = 0; iter < maxIter; iter++) { + if (!gcg->iterate()) + iter = maxIter; + } + debMsg("cgSolveWaveEq iterations:" << gcg->getIterations() << ", res:" << gcg->getSigma(), 1); + + utm1.swap(ut); + ut.copyFrom(out); + + delete gcg; +} +static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt<bool>("notiming", -1, 0); + pbPreparePlugin(parent, "cgSolveWE", !noTiming); + PyObject *_retval = 0; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock); + Grid<Real> &ut = *_args.getPtr<Grid<Real>>("ut", 1, &_lock); + Grid<Real> &utm1 = *_args.getPtr<Grid<Real>>("utm1", 2, &_lock); + Grid<Real> &out = *_args.getPtr<Grid<Real>>("out", 3, &_lock); + bool crankNic = _args.getOpt<bool>("crankNic", 4, false, &_lock); + Real cSqr = _args.getOpt<Real>("cSqr", 5, 0.25, &_lock); + Real cgMaxIterFac = _args.getOpt<Real>("cgMaxIterFac", 6, 1.5, &_lock); + Real cgAccuracy = _args.getOpt<Real>("cgAccuracy", 7, 1e-5, &_lock); + _retval = getPyNone(); + cgSolveWE(flags, ut, utm1, out, crankNic, cSqr, cgMaxIterFac, cgAccuracy); + _args.check(); + } + pbFinalizePlugin(parent, "cgSolveWE", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("cgSolveWE", e.what()); + return 0; + } +} +static const Pb::Register _RP_cgSolveWE("", "cgSolveWE", _W_3); +extern "C" { +void PbRegister_cgSolveWE() +{ + KEEP_UNUSED(_RP_cgSolveWE); +} +} + +} // namespace Manta |