This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
T
Sebastián Barschkis 4ff7c5eed6 Mantaflow [Part 1]: Added preprocessed Mantaflow source files
Includes preprocessed Mantaflow source files for both OpenMP and TBB (if OpenMP is not present, TBB files will be used instead).

These files come directly from the Mantaflow repository. Future updates to the core fluid solver will take place by updating the files.

Reviewed By: sergey, mont29

Maniphest Tasks: T59995

Differential Revision: https://developer.blender.org/D3850
2019-12-16 16:27:26 +01:00

3066 lines
98 KiB
C++

// 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> &phi;
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> &phi;
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> &phi;
};
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