// 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("notiming", -1, 0); pbPreparePlugin(parent, "sampleFlagsWithParticles", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); BasicParticleSystem &parts = *_args.getPtr("parts", 1, &_lock); const int discretization = _args.get("discretization", 2, &_lock); const Real randomness = _args.get("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("notiming", -1, 0); pbPreparePlugin(parent, "sampleLevelsetWithParticles", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const LevelsetGrid &phi = *_args.getPtr("phi", 0, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 1, &_lock); BasicParticleSystem &parts = *_args.getPtr("parts", 2, &_lock); const int discretization = _args.get("discretization", 3, &_lock); const Real randomness = _args.get("randomness", 4, &_lock); const bool reset = _args.getOpt("reset", 5, false, &_lock); const bool refillEmpty = _args.getOpt("refillEmpty", 6, false, &_lock); const int particleFlag = _args.getOpt("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("notiming", -1, 0); pbPreparePlugin(parent, "sampleShapeWithParticles", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const Shape &shape = *_args.getPtr("shape", 0, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 1, &_lock); BasicParticleSystem &parts = *_args.getPtr("parts", 2, &_lock); const int discretization = _args.get("discretization", 3, &_lock); const Real randomness = _args.get("randomness", 4, &_lock); const bool reset = _args.getOpt("reset", 5, false, &_lock); const bool refillEmpty = _args.getOpt("refillEmpty", 6, false, &_lock); const LevelsetGrid *exclude = _args.getPtrOpt("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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } FlagGrid &flags; int dummy; }; struct knSetNbObstacle : public KernelBase { knSetNbObstacle(FlagGrid &nflags, const FlagGrid &flags, const Grid &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 &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 &getArg2() { return phiObs; } typedef Grid 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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(1, maxY), *this); } FlagGrid &nflags; const FlagGrid &flags; const Grid &phiObs; }; void markFluidCells(const BasicParticleSystem &parts, FlagGrid &flags, const Grid *phiObs = NULL, const ParticleDataImpl *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("notiming", -1, 0); pbPreparePlugin(parent, "markFluidCells", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const BasicParticleSystem &parts = *_args.getPtr("parts", 0, &_lock); FlagGrid &flags = *_args.getPtr("flags", 1, &_lock); const Grid *phiObs = _args.getPtrOpt>("phiObs", 2, NULL, &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 3, NULL, &_lock); const int exclude = _args.getOpt("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 &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("notiming", -1, 0); pbPreparePlugin(parent, "testInitGridWithPos", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; Grid &grid = *_args.getPtr>("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 &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 *exclude = NULL) { // which levelset to use as threshold const Real SURFACE_LS = -1.0 * calculateRadiusFactor(phi, radiusFactor); Grid 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("notiming", -1, 0); pbPreparePlugin(parent, "adjustNumber", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem &parts = *_args.getPtr("parts", 0, &_lock); const MACGrid &vel = *_args.getPtr("vel", 1, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 2, &_lock); int minParticles = _args.get("minParticles", 3, &_lock); int maxParticles = _args.get("maxParticles", 4, &_lock); const LevelsetGrid &phi = *_args.getPtr("phi", 5, &_lock); Real radiusFactor = _args.getOpt("radiusFactor", 6, 1., &_lock); Real narrowBand = _args.getOpt("narrowBand", 7, -1., &_lock); const Grid *exclude = _args.getPtrOpt>("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 &source, Grid &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("notiming", -1, 0); pbPreparePlugin(parent, "debugIntToReal", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const Grid &source = *_args.getPtr>("source", 0, &_lock); Grid &dest = *_args.getPtr>("dest", 1, &_lock); Real factor = _args.getOpt("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 &index, Grid *counter = NULL) { bool delCounter = false; if (!counter) { counter = new Grid(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("notiming", -1, 0); pbPreparePlugin(parent, "gridParticleIndex", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const BasicParticleSystem &parts = *_args.getPtr("parts", 0, &_lock); ParticleIndexSystem &indexSys = *_args.getPtr("indexSys", 1, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 2, &_lock); Grid &index = *_args.getPtr>("index", 3, &_lock); Grid *counter = _args.getPtrOpt>("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 &index, const BasicParticleSystem &parts, const ParticleIndexSystem &indexSys, LevelsetGrid &phi, const Real radius, const ParticleDataImpl *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 &index, const BasicParticleSystem &parts, const ParticleIndexSystem &indexSys, LevelsetGrid &phi, const Real radius, const ParticleDataImpl *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 &getArg0() { return index; } typedef Grid 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 *getArg5() { return ptype; } typedef ParticleDataImpl 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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } const Grid &index; const BasicParticleSystem &parts; const ParticleIndexSystem &indexSys; LevelsetGrid φ const Real radius; const ParticleDataImpl *ptype; const int exclude; }; void unionParticleLevelset(const BasicParticleSystem &parts, const ParticleIndexSystem &indexSys, const FlagGrid &flags, const Grid &index, LevelsetGrid &phi, const Real radiusFactor = 1., const ParticleDataImpl *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("notiming", -1, 0); pbPreparePlugin(parent, "unionParticleLevelset", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const BasicParticleSystem &parts = *_args.getPtr("parts", 0, &_lock); const ParticleIndexSystem &indexSys = *_args.getPtr( "indexSys", 1, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 2, &_lock); const Grid &index = *_args.getPtr>("index", 3, &_lock); LevelsetGrid &phi = *_args.getPtr("phi", 4, &_lock); const Real radiusFactor = _args.getOpt("radiusFactor", 5, 1., &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 6, NULL, &_lock); const int exclude = _args.getOpt("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 &index, const ParticleIndexSystem &indexSys, LevelsetGrid &phi, const Real radius, const ParticleDataImpl *ptype, const int exclude, Grid *save_pAcc = NULL, Grid *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 &index, const ParticleIndexSystem &indexSys, LevelsetGrid &phi, const Real radius, const ParticleDataImpl *ptype, const int exclude, Grid *save_pAcc = NULL, Grid *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 &getArg1() { return index; } typedef Grid 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 *getArg5() { return ptype; } typedef ParticleDataImpl type5; inline const int &getArg6() { return exclude; } typedef int type6; inline Grid *getArg7() { return save_pAcc; } typedef Grid type7; inline Grid *getArg8() { return save_rAcc; } typedef Grid 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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } const BasicParticleSystem &parts; const Grid &index; const ParticleIndexSystem &indexSys; LevelsetGrid φ const Real radius; const ParticleDataImpl *ptype; const int exclude; Grid *save_pAcc; Grid *save_rAcc; }; template T smoothingValue(const Grid val, int i, int j, int k, T center) { return val(i, j, k); } // smoothing, and template struct knSmoothGrid : public KernelBase { knSmoothGrid(const Grid &me, Grid &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 &me, Grid &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 &getArg0() { return me; } typedef Grid type0; inline Grid &getArg1() { return tmp; } typedef Grid 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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(1, maxY), *this); } const Grid &me; Grid &tmp; Real factor; }; template struct knSmoothGridNeg : public KernelBase { knSmoothGridNeg(const Grid &me, Grid &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 &me, Grid &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 &getArg0() { return me; } typedef Grid type0; inline Grid &getArg1() { return tmp; } typedef Grid 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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(1, maxY), *this); } const Grid &me; Grid &tmp; Real factor; }; //! Zhu & Bridson particle level set creation void averagedParticleLevelset(const BasicParticleSystem &parts, const ParticleIndexSystem &indexSys, const FlagGrid &flags, const Grid &index, LevelsetGrid &phi, const Real radiusFactor = 1., const int smoothen = 1, const int smoothenNeg = 1, const ParticleDataImpl *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(phi, tmp, 1. / (phi.is3D() ? 7. : 5.)); phi.swap(tmp); } if (i < smoothenNeg) { knSmoothGridNeg(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("notiming", -1, 0); pbPreparePlugin(parent, "averagedParticleLevelset", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const BasicParticleSystem &parts = *_args.getPtr("parts", 0, &_lock); const ParticleIndexSystem &indexSys = *_args.getPtr( "indexSys", 1, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 2, &_lock); const Grid &index = *_args.getPtr>("index", 3, &_lock); LevelsetGrid &phi = *_args.getPtr("phi", 4, &_lock); const Real radiusFactor = _args.getOpt("radiusFactor", 5, 1., &_lock); const int smoothen = _args.getOpt("smoothen", 6, 1, &_lock); const int smoothenNeg = _args.getOpt("smoothenNeg", 7, 1, &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 8, NULL, &_lock); const int exclude = _args.getOpt("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 &pAcc, const Grid &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 &pAcc, const Grid &rAcc, const Real radius, const Real t_low, const Real t_high) const { if (rAcc(i, j, k) <= VECTOR_EPSILON) return; // outside nothing happens // 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 &getArg1() { return pAcc; } typedef Grid type1; inline const Grid &getArg2() { return rAcc; } typedef Grid 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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(1, maxY), *this); } LevelsetGrid φ const Grid &pAcc; const Grid &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 &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 *ptype = NULL, const int exclude = 0) { // create temporary grids to store values from levelset weight computation Grid save_pAcc(flags.getParent()); Grid 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(phi, tmp, 1. / (phi.is3D() ? 7. : 5.)); phi.swap(tmp); } if (i < smoothenNeg) { knSmoothGridNeg(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("notiming", -1, 0); pbPreparePlugin(parent, "improvedParticleLevelset", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const BasicParticleSystem &parts = *_args.getPtr("parts", 0, &_lock); const ParticleIndexSystem &indexSys = *_args.getPtr( "indexSys", 1, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 2, &_lock); const Grid &index = *_args.getPtr>("index", 3, &_lock); LevelsetGrid &phi = *_args.getPtr("phi", 4, &_lock); const Real radiusFactor = _args.getOpt("radiusFactor", 5, 1., &_lock); const int smoothen = _args.getOpt("smoothen", 6, 1, &_lock); const int smoothenNeg = _args.getOpt("smoothenNeg", 7, 1, &_lock); const Real t_low = _args.getOpt("t_low", 8, 0.4, &_lock); const Real t_high = _args.getOpt("t_high", 9, 3.5, &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 10, NULL, &_lock); const int exclude = _args.getOpt("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 &phiObs, const Real shift, const Real thresh, const ParticleDataImpl *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 &phiObs, const Real shift, const Real thresh, const ParticleDataImpl *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 &getArg2() { return phiObs; } typedef Grid type2; inline const Real &getArg3() { return shift; } typedef Real type3; inline const Real &getArg4() { return thresh; } typedef Real type4; inline const ParticleDataImpl *getArg5() { return ptype; } typedef ParticleDataImpl 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 &__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(0, size), *this); } BasicParticleSystem &parts; const FlagGrid &flags; const Grid &phiObs; const Real shift; const Real thresh; const ParticleDataImpl *ptype; const int exclude; }; //! push particles out of obstacle levelset void pushOutofObs(BasicParticleSystem &parts, const FlagGrid &flags, const Grid &phiObs, const Real shift = 0, const Real thresh = 0, const ParticleDataImpl *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("notiming", -1, 0); pbPreparePlugin(parent, "pushOutofObs", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; BasicParticleSystem &parts = *_args.getPtr("parts", 0, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 1, &_lock); const Grid &phiObs = *_args.getPtr>("phiObs", 2, &_lock); const Real shift = _args.getOpt("shift", 3, 0, &_lock); const Real thresh = _args.getOpt("thresh", 4, 0, &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 5, NULL, &_lock); const int exclude = _args.getOpt("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 struct knSafeDivReal : public KernelBase { knSafeDivReal(Grid &me, const Grid &other, Real cutoff = VECTOR_EPSILON) : KernelBase(&me, 0), me(me), other(other), cutoff(cutoff) { runMessage(); run(); } inline void op(IndexInt idx, Grid &me, const Grid &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 &getArg0() { return me; } typedef Grid type0; inline const Grid &getArg1() { return other; } typedef Grid 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 &__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(0, size), *this); } Grid &me; const Grid &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 &tmp, const ParticleDataImpl &pvel, const ParticleDataImpl *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 &tmp, const ParticleDataImpl &pvel, const ParticleDataImpl *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 &getArg3() { return tmp; } typedef Grid type3; inline const ParticleDataImpl &getArg4() { return pvel; } typedef ParticleDataImpl type4; inline const ParticleDataImpl *getArg5() { return ptype; } typedef ParticleDataImpl 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 &tmp; const ParticleDataImpl &pvel; const ParticleDataImpl *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 &partVel, Grid *weight = NULL, const ParticleDataImpl *ptype = NULL, const int exclude = 0) { // interpol -> grid. tmpgrid for particle contribution weights bool freeTmp = false; if (!weight) { weight = new Grid(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("notiming", -1, 0); pbPreparePlugin(parent, "mapPartsToMAC", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); MACGrid &vel = *_args.getPtr("vel", 1, &_lock); MACGrid &velOld = *_args.getPtr("velOld", 2, &_lock); const BasicParticleSystem &parts = *_args.getPtr("parts", 3, &_lock); const ParticleDataImpl &partVel = *_args.getPtr>( "partVel", 4, &_lock); Grid *weight = _args.getPtrOpt>("weight", 5, NULL, &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 6, NULL, &_lock); const int exclude = _args.getOpt("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 struct knMapLinear : public KernelBase { knMapLinear(const BasicParticleSystem &p, const FlagGrid &flags, const Grid &target, Grid >mp, const ParticleDataImpl &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 &target, Grid >mp, const ParticleDataImpl &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 &getArg2() { return target; } typedef Grid type2; inline Grid &getArg3() { return gtmp; } typedef Grid type3; inline const ParticleDataImpl &getArg4() { return psource; } typedef ParticleDataImpl 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 ⌖ Grid >mp; const ParticleDataImpl &psource; }; template void mapLinearRealHelper(const FlagGrid &flags, Grid &target, const BasicParticleSystem &parts, const ParticleDataImpl &source) { Grid tmp(flags.getParent()); target.clear(); knMapLinear(parts, flags, target, tmp, source); knSafeDivReal(target, tmp); } void mapPartsToGrid(const FlagGrid &flags, Grid &target, const BasicParticleSystem &parts, const ParticleDataImpl &source) { mapLinearRealHelper(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("notiming", -1, 0); pbPreparePlugin(parent, "mapPartsToGrid", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); Grid &target = *_args.getPtr>("target", 1, &_lock); const BasicParticleSystem &parts = *_args.getPtr("parts", 2, &_lock); const ParticleDataImpl &source = *_args.getPtr>( "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 &target, const BasicParticleSystem &parts, const ParticleDataImpl &source) { mapLinearRealHelper(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("notiming", -1, 0); pbPreparePlugin(parent, "mapPartsToGridVec3", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); Grid &target = *_args.getPtr>("target", 1, &_lock); const BasicParticleSystem &parts = *_args.getPtr("parts", 2, &_lock); const ParticleDataImpl &source = *_args.getPtr>( "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& target , BasicParticleSystem& // parts , ParticleDataImpl& source ) { mapLinearRealHelper(flags,target,parts,source); //} template struct knMapFromGrid : public KernelBase { knMapFromGrid(const BasicParticleSystem &p, const Grid &gsrc, ParticleDataImpl &target) : KernelBase(p.size()), p(p), gsrc(gsrc), target(target) { runMessage(); run(); } inline void op(IndexInt idx, const BasicParticleSystem &p, const Grid &gsrc, ParticleDataImpl &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 &getArg1() { return gsrc; } typedef Grid type1; inline ParticleDataImpl &getArg2() { return target; } typedef ParticleDataImpl type2; void runMessage() { debMsg("Executing kernel knMapFromGrid ", 3); debMsg("Kernel range" << " size " << size << " ", 4); }; void operator()(const tbb::blocked_range &__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(0, size), *this); } const BasicParticleSystem &p; const Grid &gsrc; ParticleDataImpl ⌖ }; void mapGridToParts(const Grid &source, const BasicParticleSystem &parts, ParticleDataImpl &target) { knMapFromGrid(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("notiming", -1, 0); pbPreparePlugin(parent, "mapGridToParts", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const Grid &source = *_args.getPtr>("source", 0, &_lock); const BasicParticleSystem &parts = *_args.getPtr("parts", 1, &_lock); ParticleDataImpl &target = *_args.getPtr>("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 &source, const BasicParticleSystem &parts, ParticleDataImpl &target) { knMapFromGrid(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("notiming", -1, 0); pbPreparePlugin(parent, "mapGridToPartsVec3", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const Grid &source = *_args.getPtr>("source", 0, &_lock); const BasicParticleSystem &parts = *_args.getPtr("parts", 1, &_lock); ParticleDataImpl &target = *_args.getPtr>("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 &pvel, const ParticleDataImpl *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 &pvel, const ParticleDataImpl *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 &getArg3() { return pvel; } typedef ParticleDataImpl type3; inline const ParticleDataImpl *getArg4() { return ptype; } typedef ParticleDataImpl 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 &__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(0, size), *this); } const BasicParticleSystem &p; const FlagGrid &flags; const MACGrid &vel; ParticleDataImpl &pvel; const ParticleDataImpl *ptype; const int exclude; }; void mapMACToParts(const FlagGrid &flags, const MACGrid &vel, const BasicParticleSystem &parts, ParticleDataImpl &partVel, const ParticleDataImpl *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("notiming", -1, 0); pbPreparePlugin(parent, "mapMACToParts", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); const MACGrid &vel = *_args.getPtr("vel", 1, &_lock); const BasicParticleSystem &parts = *_args.getPtr("parts", 2, &_lock); ParticleDataImpl &partVel = *_args.getPtr>( "partVel", 3, &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 4, NULL, &_lock); const int exclude = _args.getOpt("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 &pvel, const Real flipRatio, const ParticleDataImpl *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 &pvel, const Real flipRatio, const ParticleDataImpl *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 &getArg4() { return pvel; } typedef ParticleDataImpl type4; inline const Real &getArg5() { return flipRatio; } typedef Real type5; inline const ParticleDataImpl *getArg6() { return ptype; } typedef ParticleDataImpl 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 &__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(0, size), *this); } const BasicParticleSystem &p; const FlagGrid &flags; const MACGrid &vel; const MACGrid &oldVel; ParticleDataImpl &pvel; const Real flipRatio; const ParticleDataImpl *ptype; const int exclude; }; void flipVelocityUpdate(const FlagGrid &flags, const MACGrid &vel, const MACGrid &velOld, const BasicParticleSystem &parts, ParticleDataImpl &partVel, const Real flipRatio, const ParticleDataImpl *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("notiming", -1, 0); pbPreparePlugin(parent, "flipVelocityUpdate", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); const MACGrid &vel = *_args.getPtr("vel", 1, &_lock); const MACGrid &velOld = *_args.getPtr("velOld", 2, &_lock); const BasicParticleSystem &parts = *_args.getPtr("parts", 3, &_lock); ParticleDataImpl &partVel = *_args.getPtr>( "partVel", 4, &_lock); const Real flipRatio = _args.get("flipRatio", 5, &_lock); const ParticleDataImpl *ptype = _args.getPtrOpt>( "ptype", 6, NULL, &_lock); const int exclude = _args.getOpt("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 &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 &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 &getArg1() { return w; } typedef Grid 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 &__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(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } MACGrid &vel; const Grid &w; MACGrid &combineVel; const LevelsetGrid *phi; Real narrowBand; Real thresh; }; //! narrow band velocity combination void combineGridVel(MACGrid &vel, const Grid &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("notiming", -1, 0); pbPreparePlugin(parent, "combineGridVel", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; MACGrid &vel = *_args.getPtr("vel", 0, &_lock); const Grid &weight = *_args.getPtr>("weight", 1, &_lock); MACGrid &combineVel = *_args.getPtr("combineVel", 2, &_lock); const LevelsetGrid *phi = _args.getPtrOpt("phi", 3, NULL, &_lock); Real narrowBand = _args.getOpt("narrowBand", 4, 0.0, &_lock); Real thresh = _args.getOpt("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 &laplacian, const Grid &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("notiming", -1, 0); pbPreparePlugin(parent, "getLaplacian", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; Grid &laplacian = *_args.getPtr>("laplacian", 0, &_lock); const Grid &grid = *_args.getPtr>("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 &curv, const Grid &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("notiming", -1, 0); pbPreparePlugin(parent, "getCurvature", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; Grid &curv = *_args.getPtr>("curv", 0, &_lock); const Grid &grid = *_args.getPtr>("grid", 1, &_lock); const Real h = _args.getOpt("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