Includes the OpenVDB read/write functions for int grids. This essential for the resume bake functionality in modular fluid caches.
3063 lines
98 KiB
C++
3063 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;
|
|
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);
|
|
|
|
// spray particle
|
|
if (neighborRatio(gridpos) < c_s) {
|
|
pts_sec[idx].flag |= ParticleBase::PSPRAY;
|
|
pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PFOAM);
|
|
v_sec[idx] += dt *
|
|
((f_sec[idx] / 1) + gravity); // TODO: if forces are added (e.g. fluid
|
|
// guiding), add parameter for mass instead of 1
|
|
|
|
// anti tunneling for small obstacles
|
|
for (int ct = 1; ct < antitunneling; ct++) {
|
|
Vec3i tempPos = toVec3i(pts_sec[idx].pos +
|
|
ct * (1 / Real(antitunneling)) * dt * v_sec[idx]);
|
|
if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) {
|
|
pts_sec.kill(idx);
|
|
return;
|
|
}
|
|
}
|
|
pts_sec[idx].pos += dt * v_sec[idx];
|
|
}
|
|
|
|
// air bubble particle
|
|
else if (neighborRatio(gridpos) > c_b) {
|
|
pts_sec[idx].flag |= ParticleBase::PBUBBLE;
|
|
pts_sec[idx].flag &= ~(ParticleBase::PSPRAY | ParticleBase::PFOAM);
|
|
|
|
const Vec3 vj = (v.getInterpolated(pts_sec[idx].pos) - v_sec[idx]) / dt;
|
|
v_sec[idx] += dt * (k_b * -gravity + k_d * vj);
|
|
|
|
// anti tunneling for small obstacles
|
|
for (int ct = 1; ct < antitunneling; ct++) {
|
|
Vec3i tempPos = toVec3i(pts_sec[idx].pos +
|
|
ct * (1 / Real(antitunneling)) * dt * v_sec[idx]);
|
|
if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) {
|
|
pts_sec.kill(idx);
|
|
return;
|
|
}
|
|
}
|
|
pts_sec[idx].pos += dt * v_sec[idx];
|
|
}
|
|
|
|
// foam particle
|
|
else {
|
|
pts_sec[idx].flag |= ParticleBase::PFOAM;
|
|
pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PSPRAY);
|
|
|
|
const Vec3 vj = v.getInterpolated(pts_sec[idx].pos);
|
|
// anti tunneling for small obstacles
|
|
for (int ct = 1; ct < antitunneling; ct++) {
|
|
Vec3i tempPos = toVec3i(pts_sec[idx].pos + ct * (1 / Real(antitunneling)) * dt * vj);
|
|
if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) {
|
|
pts_sec.kill(idx);
|
|
return;
|
|
}
|
|
}
|
|
pts_sec[idx].pos += dt * v.getInterpolated(pts_sec[idx].pos);
|
|
}
|
|
|
|
// lifetime
|
|
l_sec[idx] -= dt;
|
|
if (l_sec[idx] <= Real(0)) {
|
|
pts_sec.kill(idx);
|
|
}
|
|
}
|
|
inline BasicParticleSystem &getArg0()
|
|
{
|
|
return pts_sec;
|
|
}
|
|
typedef BasicParticleSystem type0;
|
|
inline ParticleDataImpl<Vec3> &getArg1()
|
|
{
|
|
return v_sec;
|
|
}
|
|
typedef ParticleDataImpl<Vec3> type1;
|
|
inline ParticleDataImpl<Real> &getArg2()
|
|
{
|
|
return l_sec;
|
|
}
|
|
typedef ParticleDataImpl<Real> type2;
|
|
inline const ParticleDataImpl<Vec3> &getArg3()
|
|
{
|
|
return f_sec;
|
|
}
|
|
typedef ParticleDataImpl<Vec3> type3;
|
|
inline const FlagGrid &getArg4()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type4;
|
|
inline const MACGrid &getArg5()
|
|
{
|
|
return v;
|
|
}
|
|
typedef MACGrid type5;
|
|
inline const Grid<Real> &getArg6()
|
|
{
|
|
return neighborRatio;
|
|
}
|
|
typedef Grid<Real> type6;
|
|
inline const Vec3 &getArg7()
|
|
{
|
|
return gravity;
|
|
}
|
|
typedef Vec3 type7;
|
|
inline const Real &getArg8()
|
|
{
|
|
return k_b;
|
|
}
|
|
typedef Real type8;
|
|
inline const Real &getArg9()
|
|
{
|
|
return k_d;
|
|
}
|
|
typedef Real type9;
|
|
inline const Real &getArg10()
|
|
{
|
|
return c_s;
|
|
}
|
|
typedef Real type10;
|
|
inline const Real &getArg11()
|
|
{
|
|
return c_b;
|
|
}
|
|
typedef Real type11;
|
|
inline const Real &getArg12()
|
|
{
|
|
return dt;
|
|
}
|
|
typedef Real type12;
|
|
inline const int &getArg13()
|
|
{
|
|
return exclude;
|
|
}
|
|
typedef int type13;
|
|
inline const int &getArg14()
|
|
{
|
|
return antitunneling;
|
|
}
|
|
typedef int type14;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipUpdateSecondaryParticlesLinear ", 3);
|
|
debMsg("Kernel range"
|
|
<< " size " << size << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
|
|
op(idx,
|
|
pts_sec,
|
|
v_sec,
|
|
l_sec,
|
|
f_sec,
|
|
flags,
|
|
v,
|
|
neighborRatio,
|
|
gravity,
|
|
k_b,
|
|
k_d,
|
|
c_s,
|
|
c_b,
|
|
dt,
|
|
exclude,
|
|
antitunneling);
|
|
}
|
|
void run()
|
|
{
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
|
|
}
|
|
BasicParticleSystem &pts_sec;
|
|
ParticleDataImpl<Vec3> &v_sec;
|
|
ParticleDataImpl<Real> &l_sec;
|
|
const ParticleDataImpl<Vec3> &f_sec;
|
|
const FlagGrid &flags;
|
|
const MACGrid &v;
|
|
const Grid<Real> &neighborRatio;
|
|
const Vec3 gravity;
|
|
const Real k_b;
|
|
const Real k_d;
|
|
const Real c_s;
|
|
const Real c_b;
|
|
const Real dt;
|
|
const int exclude;
|
|
const int antitunneling;
|
|
};
|
|
// updates position &pts_sec.pos and velocity &v_sec of secondary particles according to the
|
|
// particle type determined by the neighbor ratio with cubic spline interpolation
|
|
|
|
struct knFlipUpdateSecondaryParticlesCubic : public KernelBase {
|
|
knFlipUpdateSecondaryParticlesCubic(BasicParticleSystem &pts_sec,
|
|
ParticleDataImpl<Vec3> &v_sec,
|
|
ParticleDataImpl<Real> &l_sec,
|
|
const ParticleDataImpl<Vec3> &f_sec,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const Grid<Real> &neighborRatio,
|
|
const int radius,
|
|
const Vec3 gravity,
|
|
const Real k_b,
|
|
const Real k_d,
|
|
const Real c_s,
|
|
const Real c_b,
|
|
const Real dt,
|
|
const int exclude,
|
|
const int antitunneling,
|
|
const int itype)
|
|
: KernelBase(pts_sec.size()),
|
|
pts_sec(pts_sec),
|
|
v_sec(v_sec),
|
|
l_sec(l_sec),
|
|
f_sec(f_sec),
|
|
flags(flags),
|
|
v(v),
|
|
neighborRatio(neighborRatio),
|
|
radius(radius),
|
|
gravity(gravity),
|
|
k_b(k_b),
|
|
k_d(k_d),
|
|
c_s(c_s),
|
|
c_b(c_b),
|
|
dt(dt),
|
|
exclude(exclude),
|
|
antitunneling(antitunneling),
|
|
itype(itype)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(IndexInt idx,
|
|
BasicParticleSystem &pts_sec,
|
|
ParticleDataImpl<Vec3> &v_sec,
|
|
ParticleDataImpl<Real> &l_sec,
|
|
const ParticleDataImpl<Vec3> &f_sec,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const Grid<Real> &neighborRatio,
|
|
const int radius,
|
|
const Vec3 gravity,
|
|
const Real k_b,
|
|
const Real k_d,
|
|
const Real c_s,
|
|
const Real c_b,
|
|
const Real dt,
|
|
const int exclude,
|
|
const int antitunneling,
|
|
const int itype) const
|
|
{
|
|
|
|
if (!pts_sec.isActive(idx) || pts_sec[idx].flag & exclude)
|
|
return;
|
|
if (!flags.isInBounds(pts_sec[idx].pos)) {
|
|
pts_sec.kill(idx);
|
|
return;
|
|
}
|
|
|
|
Vec3i gridpos = toVec3i(pts_sec[idx].pos);
|
|
int i = gridpos.x;
|
|
int j = gridpos.y;
|
|
int k = gridpos.z;
|
|
|
|
// spray particle
|
|
if (neighborRatio(gridpos) < c_s) {
|
|
pts_sec[idx].flag |= ParticleBase::PSPRAY;
|
|
pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PFOAM);
|
|
v_sec[idx] += dt *
|
|
((f_sec[idx] / 1) + gravity); // TODO: if forces are added (e.g. fluid
|
|
// guiding), add parameter for mass instead of 1
|
|
|
|
// anti tunneling for small obstacles
|
|
for (int ct = 1; ct < antitunneling; ct++) {
|
|
Vec3i tempPos = toVec3i(pts_sec[idx].pos +
|
|
ct * (1 / Real(antitunneling)) * dt * v_sec[idx]);
|
|
if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) {
|
|
pts_sec.kill(idx);
|
|
return;
|
|
}
|
|
}
|
|
pts_sec[idx].pos += dt * v_sec[idx];
|
|
}
|
|
|
|
// air bubble particle
|
|
else if (neighborRatio(gridpos) > c_b) {
|
|
pts_sec[idx].flag |= ParticleBase::PBUBBLE;
|
|
pts_sec[idx].flag &= ~(ParticleBase::PSPRAY | ParticleBase::PFOAM);
|
|
const Vec3 &xi = pts_sec[idx].pos;
|
|
Vec3 sumNumerator = Vec3(0, 0, 0);
|
|
Real sumDenominator = 0;
|
|
for (IndexInt x = i - radius; x <= i + radius; x++) {
|
|
for (IndexInt y = j - radius; y <= j + radius; y++) {
|
|
for (IndexInt z = k - radius; z <= k + radius; z++) {
|
|
Vec3i xj = Vec3i(x, y, z);
|
|
if ((x == i && y == j && z == k) || !flags.isInBounds(xj))
|
|
continue;
|
|
if (!(flags(xj) & itype))
|
|
continue;
|
|
const Real len_xij = norm(xi - Vec3(x, y, z));
|
|
|
|
int dim = flags.is3D() ? 3 : 2;
|
|
Real dist = flags.is3D() ? 1.732 : 1.414;
|
|
Real weight = cubicSpline(radius * dist, len_xij, dim);
|
|
sumNumerator += v.getCentered(xj) *
|
|
weight; // estimate next position by current velocity
|
|
sumDenominator += weight;
|
|
}
|
|
}
|
|
}
|
|
const Vec3 temp = ((sumNumerator / sumDenominator) - v_sec[idx]) / dt;
|
|
v_sec[idx] += dt * (k_b * -gravity + k_d * temp);
|
|
|
|
// anti tunneling for small obstacles
|
|
for (int ct = 1; ct < antitunneling; ct++) {
|
|
Vec3i tempPos = toVec3i(pts_sec[idx].pos +
|
|
ct * (1 / Real(antitunneling)) * dt * v_sec[idx]);
|
|
if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) {
|
|
pts_sec.kill(idx);
|
|
return;
|
|
}
|
|
}
|
|
pts_sec[idx].pos += dt * v_sec[idx];
|
|
}
|
|
|
|
// foam particle
|
|
else {
|
|
pts_sec[idx].flag |= ParticleBase::PFOAM;
|
|
pts_sec[idx].flag &= ~(ParticleBase::PBUBBLE | ParticleBase::PSPRAY);
|
|
const Vec3 &xi = pts_sec[idx].pos;
|
|
Vec3 sumNumerator = Vec3(0, 0, 0);
|
|
Real sumDenominator = 0;
|
|
for (IndexInt x = i - radius; x <= i + radius; x++) {
|
|
for (IndexInt y = j - radius; y <= j + radius; y++) {
|
|
for (IndexInt z = k - radius; z <= k + radius; z++) {
|
|
Vec3i xj = Vec3i(x, y, z);
|
|
if ((x == i && y == j && z == k) || !flags.isInBounds(xj))
|
|
continue;
|
|
if (!(flags(xj) & itype))
|
|
continue;
|
|
const Real len_xij = norm(xi - Vec3(x, y, z));
|
|
|
|
int dim = flags.is3D() ? 3 : 2;
|
|
Real dist = flags.is3D() ? 1.732 : 1.414;
|
|
Real weight = cubicSpline(radius * dist, len_xij, dim);
|
|
sumNumerator += v.getCentered(xj) *
|
|
weight; // estimate next position by current velocity
|
|
sumDenominator += weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
// anti tunneling for small obstacles
|
|
for (int ct = 1; ct < antitunneling; ct++) {
|
|
Vec3i tempPos = toVec3i(pts_sec[idx].pos + ct * (1 / Real(antitunneling)) * dt *
|
|
(sumNumerator / sumDenominator));
|
|
if (!flags.isInBounds(tempPos) || flags(tempPos) & FlagGrid::TypeObstacle) {
|
|
pts_sec.kill(idx);
|
|
return;
|
|
}
|
|
}
|
|
pts_sec[idx].pos += dt * (sumNumerator / sumDenominator);
|
|
}
|
|
|
|
// lifetime
|
|
l_sec[idx] -= dt;
|
|
if (l_sec[idx] <= Real(0)) {
|
|
pts_sec.kill(idx);
|
|
}
|
|
}
|
|
inline BasicParticleSystem &getArg0()
|
|
{
|
|
return pts_sec;
|
|
}
|
|
typedef BasicParticleSystem type0;
|
|
inline ParticleDataImpl<Vec3> &getArg1()
|
|
{
|
|
return v_sec;
|
|
}
|
|
typedef ParticleDataImpl<Vec3> type1;
|
|
inline ParticleDataImpl<Real> &getArg2()
|
|
{
|
|
return l_sec;
|
|
}
|
|
typedef ParticleDataImpl<Real> type2;
|
|
inline const ParticleDataImpl<Vec3> &getArg3()
|
|
{
|
|
return f_sec;
|
|
}
|
|
typedef ParticleDataImpl<Vec3> type3;
|
|
inline const FlagGrid &getArg4()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type4;
|
|
inline const MACGrid &getArg5()
|
|
{
|
|
return v;
|
|
}
|
|
typedef MACGrid type5;
|
|
inline const Grid<Real> &getArg6()
|
|
{
|
|
return neighborRatio;
|
|
}
|
|
typedef Grid<Real> type6;
|
|
inline const int &getArg7()
|
|
{
|
|
return radius;
|
|
}
|
|
typedef int type7;
|
|
inline const Vec3 &getArg8()
|
|
{
|
|
return gravity;
|
|
}
|
|
typedef Vec3 type8;
|
|
inline const Real &getArg9()
|
|
{
|
|
return k_b;
|
|
}
|
|
typedef Real type9;
|
|
inline const Real &getArg10()
|
|
{
|
|
return k_d;
|
|
}
|
|
typedef Real type10;
|
|
inline const Real &getArg11()
|
|
{
|
|
return c_s;
|
|
}
|
|
typedef Real type11;
|
|
inline const Real &getArg12()
|
|
{
|
|
return c_b;
|
|
}
|
|
typedef Real type12;
|
|
inline const Real &getArg13()
|
|
{
|
|
return dt;
|
|
}
|
|
typedef Real type13;
|
|
inline const int &getArg14()
|
|
{
|
|
return exclude;
|
|
}
|
|
typedef int type14;
|
|
inline const int &getArg15()
|
|
{
|
|
return antitunneling;
|
|
}
|
|
typedef int type15;
|
|
inline const int &getArg16()
|
|
{
|
|
return itype;
|
|
}
|
|
typedef int type16;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipUpdateSecondaryParticlesCubic ", 3);
|
|
debMsg("Kernel range"
|
|
<< " size " << size << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
|
|
op(idx,
|
|
pts_sec,
|
|
v_sec,
|
|
l_sec,
|
|
f_sec,
|
|
flags,
|
|
v,
|
|
neighborRatio,
|
|
radius,
|
|
gravity,
|
|
k_b,
|
|
k_d,
|
|
c_s,
|
|
c_b,
|
|
dt,
|
|
exclude,
|
|
antitunneling,
|
|
itype);
|
|
}
|
|
void run()
|
|
{
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
|
|
}
|
|
BasicParticleSystem &pts_sec;
|
|
ParticleDataImpl<Vec3> &v_sec;
|
|
ParticleDataImpl<Real> &l_sec;
|
|
const ParticleDataImpl<Vec3> &f_sec;
|
|
const FlagGrid &flags;
|
|
const MACGrid &v;
|
|
const Grid<Real> &neighborRatio;
|
|
const int radius;
|
|
const Vec3 gravity;
|
|
const Real k_b;
|
|
const Real k_d;
|
|
const Real c_s;
|
|
const Real c_b;
|
|
const Real dt;
|
|
const int exclude;
|
|
const int antitunneling;
|
|
const int itype;
|
|
};
|
|
|
|
void flipUpdateSecondaryParticles(const std::string mode,
|
|
BasicParticleSystem &pts_sec,
|
|
ParticleDataImpl<Vec3> &v_sec,
|
|
ParticleDataImpl<Real> &l_sec,
|
|
const ParticleDataImpl<Vec3> &f_sec,
|
|
FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const Grid<Real> &neighborRatio,
|
|
const int radius,
|
|
const Vec3 gravity,
|
|
const Real k_b,
|
|
const Real k_d,
|
|
const Real c_s,
|
|
const Real c_b,
|
|
const Real dt,
|
|
const int exclude = ParticleBase::PTRACER,
|
|
const int antitunneling = 0,
|
|
const int itype = FlagGrid::TypeFluid)
|
|
{
|
|
|
|
Vec3 g = gravity / flags.getDx();
|
|
if (mode == "linear") {
|
|
knFlipUpdateSecondaryParticlesLinear(pts_sec,
|
|
v_sec,
|
|
l_sec,
|
|
f_sec,
|
|
flags,
|
|
v,
|
|
neighborRatio,
|
|
g,
|
|
k_b,
|
|
k_d,
|
|
c_s,
|
|
c_b,
|
|
dt,
|
|
exclude,
|
|
antitunneling);
|
|
}
|
|
else if (mode == "cubic") {
|
|
knFlipUpdateSecondaryParticlesCubic(pts_sec,
|
|
v_sec,
|
|
l_sec,
|
|
f_sec,
|
|
flags,
|
|
v,
|
|
neighborRatio,
|
|
radius,
|
|
g,
|
|
k_b,
|
|
k_d,
|
|
c_s,
|
|
c_b,
|
|
dt,
|
|
exclude,
|
|
antitunneling,
|
|
itype);
|
|
}
|
|
else {
|
|
throw std::invalid_argument("Unknown mode: use \"linear\" or \"cubic\" instead!");
|
|
}
|
|
pts_sec.doCompress();
|
|
}
|
|
static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "flipUpdateSecondaryParticles", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
const std::string mode = _args.get<std::string>("mode", 0, &_lock);
|
|
BasicParticleSystem &pts_sec = *_args.getPtr<BasicParticleSystem>("pts_sec", 1, &_lock);
|
|
ParticleDataImpl<Vec3> &v_sec = *_args.getPtr<ParticleDataImpl<Vec3>>("v_sec", 2, &_lock);
|
|
ParticleDataImpl<Real> &l_sec = *_args.getPtr<ParticleDataImpl<Real>>("l_sec", 3, &_lock);
|
|
const ParticleDataImpl<Vec3> &f_sec = *_args.getPtr<ParticleDataImpl<Vec3>>(
|
|
"f_sec", 4, &_lock);
|
|
FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 5, &_lock);
|
|
const MACGrid &v = *_args.getPtr<MACGrid>("v", 6, &_lock);
|
|
const Grid<Real> &neighborRatio = *_args.getPtr<Grid<Real>>("neighborRatio", 7, &_lock);
|
|
const int radius = _args.get<int>("radius", 8, &_lock);
|
|
const Vec3 gravity = _args.get<Vec3>("gravity", 9, &_lock);
|
|
const Real k_b = _args.get<Real>("k_b", 10, &_lock);
|
|
const Real k_d = _args.get<Real>("k_d", 11, &_lock);
|
|
const Real c_s = _args.get<Real>("c_s", 12, &_lock);
|
|
const Real c_b = _args.get<Real>("c_b", 13, &_lock);
|
|
const Real dt = _args.get<Real>("dt", 14, &_lock);
|
|
const int exclude = _args.getOpt<int>("exclude", 15, ParticleBase::PTRACER, &_lock);
|
|
const int antitunneling = _args.getOpt<int>("antitunneling", 16, 0, &_lock);
|
|
const int itype = _args.getOpt<int>("itype", 17, FlagGrid::TypeFluid, &_lock);
|
|
_retval = getPyNone();
|
|
flipUpdateSecondaryParticles(mode,
|
|
pts_sec,
|
|
v_sec,
|
|
l_sec,
|
|
f_sec,
|
|
flags,
|
|
v,
|
|
neighborRatio,
|
|
radius,
|
|
gravity,
|
|
k_b,
|
|
k_d,
|
|
c_s,
|
|
c_b,
|
|
dt,
|
|
exclude,
|
|
antitunneling,
|
|
itype);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "flipUpdateSecondaryParticles", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("flipUpdateSecondaryParticles", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_flipUpdateSecondaryParticles("",
|
|
"flipUpdateSecondaryParticles",
|
|
_W_2);
|
|
extern "C" {
|
|
void PbRegister_flipUpdateSecondaryParticles()
|
|
{
|
|
KEEP_UNUSED(_RP_flipUpdateSecondaryParticles);
|
|
}
|
|
}
|
|
|
|
// removes secondary particles in &pts_sec that are inside boundaries (cells that are marked as
|
|
// obstacle/outflow in &flags)
|
|
|
|
struct knFlipDeleteParticlesInObstacle : public KernelBase {
|
|
knFlipDeleteParticlesInObstacle(BasicParticleSystem &pts, const FlagGrid &flags)
|
|
: KernelBase(pts.size()), pts(pts), flags(flags)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(IndexInt idx, BasicParticleSystem &pts, const FlagGrid &flags) const
|
|
{
|
|
|
|
if (!pts.isActive(idx))
|
|
return;
|
|
|
|
const Vec3 &xi = pts[idx].pos;
|
|
const Vec3i xidx = toVec3i(xi);
|
|
// remove particles that completely left the bounds
|
|
if (!flags.isInBounds(xidx)) {
|
|
pts.kill(idx);
|
|
return;
|
|
}
|
|
int gridIndex = flags.index(xidx);
|
|
// remove particles that penetrate obstacles
|
|
if (flags[gridIndex] == FlagGrid::TypeObstacle || flags[gridIndex] == FlagGrid::TypeOutflow) {
|
|
pts.kill(idx);
|
|
}
|
|
}
|
|
inline BasicParticleSystem &getArg0()
|
|
{
|
|
return pts;
|
|
}
|
|
typedef BasicParticleSystem type0;
|
|
inline const FlagGrid &getArg1()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type1;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipDeleteParticlesInObstacle ", 3);
|
|
debMsg("Kernel range"
|
|
<< " size " << size << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
|
|
op(idx, pts, flags);
|
|
}
|
|
void run()
|
|
{
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
|
|
}
|
|
BasicParticleSystem &pts;
|
|
const FlagGrid &flags;
|
|
};
|
|
|
|
void flipDeleteParticlesInObstacle(BasicParticleSystem &pts, const FlagGrid &flags)
|
|
{
|
|
|
|
knFlipDeleteParticlesInObstacle(pts, flags);
|
|
pts.doCompress();
|
|
}
|
|
static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "flipDeleteParticlesInObstacle", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
BasicParticleSystem &pts = *_args.getPtr<BasicParticleSystem>("pts", 0, &_lock);
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock);
|
|
_retval = getPyNone();
|
|
flipDeleteParticlesInObstacle(pts, flags);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "flipDeleteParticlesInObstacle", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("flipDeleteParticlesInObstacle", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_flipDeleteParticlesInObstacle("",
|
|
"flipDeleteParticlesInObstacle",
|
|
_W_3);
|
|
extern "C" {
|
|
void PbRegister_flipDeleteParticlesInObstacle()
|
|
{
|
|
KEEP_UNUSED(_RP_flipDeleteParticlesInObstacle);
|
|
}
|
|
}
|
|
|
|
// helper method to debug statistical data from grid
|
|
|
|
void debugGridInfo(const FlagGrid &flags,
|
|
Grid<Real> &grid,
|
|
std::string name,
|
|
const int itype = FlagGrid::TypeFluid)
|
|
{
|
|
FluidSolver *s = flags.getParent();
|
|
int countFluid = 0;
|
|
int countLargerZero = 0;
|
|
Real avg = 0;
|
|
Real max = 0;
|
|
Real sum = 0;
|
|
Real avgLargerZero = 0;
|
|
FOR_IJK_BND(grid, 1)
|
|
{
|
|
if (!(flags(i, j, k) & itype))
|
|
continue;
|
|
countFluid++;
|
|
if (grid(i, j, k) > 0)
|
|
countLargerZero++;
|
|
sum += grid(i, j, k);
|
|
if (grid(i, j, k) > max)
|
|
max = grid(i, j, k);
|
|
}
|
|
avg = sum / std::max(Real(countFluid), Real(1));
|
|
avgLargerZero = sum / std::max(Real(countLargerZero), Real(1));
|
|
|
|
debMsg("Step: " << s->mFrame << " - Grid " << name << "\n\tcountFluid \t\t" << countFluid
|
|
<< "\n\tcountLargerZero \t" << countLargerZero << "\n\tsum \t\t\t" << sum
|
|
<< "\n\tavg \t\t\t" << avg << "\n\tavgLargerZero \t\t" << avgLargerZero
|
|
<< "\n\tmax \t\t\t" << max,
|
|
1);
|
|
}
|
|
static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "debugGridInfo", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
Grid<Real> &grid = *_args.getPtr<Grid<Real>>("grid", 1, &_lock);
|
|
std::string name = _args.get<std::string>("name", 2, &_lock);
|
|
const int itype = _args.getOpt<int>("itype", 3, FlagGrid::TypeFluid, &_lock);
|
|
_retval = getPyNone();
|
|
debugGridInfo(flags, grid, name, itype);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "debugGridInfo", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("debugGridInfo", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_debugGridInfo("", "debugGridInfo", _W_4);
|
|
extern "C" {
|
|
void PbRegister_debugGridInfo()
|
|
{
|
|
KEEP_UNUSED(_RP_debugGridInfo);
|
|
}
|
|
}
|
|
|
|
// The following methods are helper functions to recreate the velocity and flag grid from the
|
|
// underlying FLIP simulation. They cannot simply be loaded because of the upres to a higher
|
|
// resolution, instead a levelset is used.
|
|
|
|
struct knSetFlagsFromLevelset : public KernelBase {
|
|
knSetFlagsFromLevelset(FlagGrid &flags,
|
|
const Grid<Real> &phi,
|
|
const int exclude = FlagGrid::TypeObstacle,
|
|
const int itype = FlagGrid::TypeFluid)
|
|
: KernelBase(&flags, 0), flags(flags), phi(phi), exclude(exclude), itype(itype)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(IndexInt idx,
|
|
FlagGrid &flags,
|
|
const Grid<Real> &phi,
|
|
const int exclude = FlagGrid::TypeObstacle,
|
|
const int itype = FlagGrid::TypeFluid) const
|
|
{
|
|
if (phi(idx) < 0 && !(flags(idx) & exclude))
|
|
flags(idx) = itype;
|
|
}
|
|
inline FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline const Grid<Real> &getArg1()
|
|
{
|
|
return phi;
|
|
}
|
|
typedef Grid<Real> type1;
|
|
inline const int &getArg2()
|
|
{
|
|
return exclude;
|
|
}
|
|
typedef int type2;
|
|
inline const int &getArg3()
|
|
{
|
|
return itype;
|
|
}
|
|
typedef int type3;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knSetFlagsFromLevelset ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
|
|
op(idx, flags, phi, exclude, itype);
|
|
}
|
|
void run()
|
|
{
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
|
|
}
|
|
FlagGrid &flags;
|
|
const Grid<Real> φ
|
|
const int exclude;
|
|
const int itype;
|
|
};
|
|
|
|
void setFlagsFromLevelset(FlagGrid &flags,
|
|
const Grid<Real> &phi,
|
|
const int exclude = FlagGrid::TypeObstacle,
|
|
const int itype = FlagGrid::TypeFluid)
|
|
{
|
|
knSetFlagsFromLevelset(flags, phi, exclude, itype);
|
|
}
|
|
static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "setFlagsFromLevelset", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
const Grid<Real> &phi = *_args.getPtr<Grid<Real>>("phi", 1, &_lock);
|
|
const int exclude = _args.getOpt<int>("exclude", 2, FlagGrid::TypeObstacle, &_lock);
|
|
const int itype = _args.getOpt<int>("itype", 3, FlagGrid::TypeFluid, &_lock);
|
|
_retval = getPyNone();
|
|
setFlagsFromLevelset(flags, phi, exclude, itype);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "setFlagsFromLevelset", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("setFlagsFromLevelset", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_setFlagsFromLevelset("", "setFlagsFromLevelset", _W_5);
|
|
extern "C" {
|
|
void PbRegister_setFlagsFromLevelset()
|
|
{
|
|
KEEP_UNUSED(_RP_setFlagsFromLevelset);
|
|
}
|
|
}
|
|
|
|
struct knSetMACFromLevelset : public KernelBase {
|
|
knSetMACFromLevelset(MACGrid &v, const Grid<Real> &phi, const Vec3 c)
|
|
: KernelBase(&v, 0), v(v), phi(phi), c(c)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i, int j, int k, MACGrid &v, const Grid<Real> &phi, const Vec3 c) const
|
|
{
|
|
if (phi.getInterpolated(Vec3(i, j, k)) > 0)
|
|
v(i, j, k) = c;
|
|
}
|
|
inline MACGrid &getArg0()
|
|
{
|
|
return v;
|
|
}
|
|
typedef MACGrid type0;
|
|
inline const Grid<Real> &getArg1()
|
|
{
|
|
return phi;
|
|
}
|
|
typedef Grid<Real> type1;
|
|
inline const Vec3 &getArg2()
|
|
{
|
|
return c;
|
|
}
|
|
typedef Vec3 type2;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knSetMACFromLevelset ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 0; j < _maxY; j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, v, phi, c);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, v, phi, c);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this);
|
|
}
|
|
MACGrid &v;
|
|
const Grid<Real> φ
|
|
const Vec3 c;
|
|
};
|
|
|
|
void setMACFromLevelset(MACGrid &v, const Grid<Real> &phi, const Vec3 c)
|
|
{
|
|
knSetMACFromLevelset(v, phi, c);
|
|
}
|
|
static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "setMACFromLevelset", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
MACGrid &v = *_args.getPtr<MACGrid>("v", 0, &_lock);
|
|
const Grid<Real> &phi = *_args.getPtr<Grid<Real>>("phi", 1, &_lock);
|
|
const Vec3 c = _args.get<Vec3>("c", 2, &_lock);
|
|
_retval = getPyNone();
|
|
setMACFromLevelset(v, phi, c);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "setMACFromLevelset", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("setMACFromLevelset", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_setMACFromLevelset("", "setMACFromLevelset", _W_6);
|
|
extern "C" {
|
|
void PbRegister_setMACFromLevelset()
|
|
{
|
|
KEEP_UNUSED(_RP_setMACFromLevelset);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
// END Secondary Particles for FLIP
|
|
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
#pragma endregion
|
|
|
|
#pragma region Legacy Methods(still useful for debugging)
|
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
|
//-----------------
|
|
// Legacy Methods (still useful for debugging)
|
|
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead!
|
|
// computes trapped air potential for all fluid cells in &flags and saves it in &pot
|
|
|
|
struct knFlipComputePotentialTrappedAir : public KernelBase {
|
|
knFlipComputePotentialTrappedAir(Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const int radius,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeFluid)
|
|
: KernelBase(&pot, 1),
|
|
pot(pot),
|
|
flags(flags),
|
|
v(v),
|
|
radius(radius),
|
|
tauMin(tauMin),
|
|
tauMax(tauMax),
|
|
scaleFromManta(scaleFromManta),
|
|
itype(itype),
|
|
jtype(jtype)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const int radius,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeFluid) const
|
|
{
|
|
|
|
if (!(flags(i, j, k) & itype))
|
|
return;
|
|
|
|
const Vec3 &xi = scaleFromManta * Vec3(i, j, k); // scale to unit cube
|
|
const Vec3 &vi = scaleFromManta * v.getCentered(i, j, k);
|
|
Real vdiff = 0;
|
|
for (IndexInt x = i - radius; x <= i + radius; x++) {
|
|
for (IndexInt y = j - radius; y <= j + radius; y++) {
|
|
for (IndexInt z = k - radius; z <= k + radius; z++) {
|
|
if ((x == i && y == j && z == k) || !(flags(x, y, z) & jtype))
|
|
continue;
|
|
|
|
const Vec3 &xj = scaleFromManta * Vec3(x, y, z); // scale to unit cube
|
|
const Vec3 &vj = scaleFromManta * v.getCentered(x, y, z);
|
|
const Vec3 xij = xi - xj;
|
|
const Vec3 vij = vi - vj;
|
|
Real h = !pot.is3D() ? 1.414 * radius :
|
|
1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius
|
|
// for h, due to squared resp. cubic neighbor area
|
|
vdiff += norm(vij) * (1 - dot(getNormalized(vij), getNormalized(xij))) *
|
|
(1 - norm(xij) / h);
|
|
}
|
|
}
|
|
}
|
|
pot(i, j, k) = (std::min(vdiff, tauMax) - std::min(vdiff, tauMin)) / (tauMax - tauMin);
|
|
}
|
|
inline Grid<Real> &getArg0()
|
|
{
|
|
return pot;
|
|
}
|
|
typedef Grid<Real> type0;
|
|
inline const FlagGrid &getArg1()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type1;
|
|
inline const MACGrid &getArg2()
|
|
{
|
|
return v;
|
|
}
|
|
typedef MACGrid type2;
|
|
inline const int &getArg3()
|
|
{
|
|
return radius;
|
|
}
|
|
typedef int type3;
|
|
inline const Real &getArg4()
|
|
{
|
|
return tauMin;
|
|
}
|
|
typedef Real type4;
|
|
inline const Real &getArg5()
|
|
{
|
|
return tauMax;
|
|
}
|
|
typedef Real type5;
|
|
inline const Real &getArg6()
|
|
{
|
|
return scaleFromManta;
|
|
}
|
|
typedef Real type6;
|
|
inline const int &getArg7()
|
|
{
|
|
return itype;
|
|
}
|
|
typedef int type7;
|
|
inline const int &getArg8()
|
|
{
|
|
return jtype;
|
|
}
|
|
typedef int type8;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipComputePotentialTrappedAir ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
Grid<Real> &pot;
|
|
const FlagGrid &flags;
|
|
const MACGrid &v;
|
|
const int radius;
|
|
const Real tauMin;
|
|
const Real tauMax;
|
|
const Real scaleFromManta;
|
|
const int itype;
|
|
const int jtype;
|
|
};
|
|
|
|
void flipComputePotentialTrappedAir(Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const int radius,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeFluid)
|
|
{
|
|
pot.clear();
|
|
knFlipComputePotentialTrappedAir(
|
|
pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype);
|
|
}
|
|
static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "flipComputePotentialTrappedAir", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
Grid<Real> &pot = *_args.getPtr<Grid<Real>>("pot", 0, &_lock);
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock);
|
|
const MACGrid &v = *_args.getPtr<MACGrid>("v", 2, &_lock);
|
|
const int radius = _args.get<int>("radius", 3, &_lock);
|
|
const Real tauMin = _args.get<Real>("tauMin", 4, &_lock);
|
|
const Real tauMax = _args.get<Real>("tauMax", 5, &_lock);
|
|
const Real scaleFromManta = _args.get<Real>("scaleFromManta", 6, &_lock);
|
|
const int itype = _args.getOpt<int>("itype", 7, FlagGrid::TypeFluid, &_lock);
|
|
const int jtype = _args.getOpt<int>("jtype", 8, FlagGrid::TypeFluid, &_lock);
|
|
_retval = getPyNone();
|
|
flipComputePotentialTrappedAir(
|
|
pot, flags, v, radius, tauMin, tauMax, scaleFromManta, itype, jtype);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "flipComputePotentialTrappedAir", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("flipComputePotentialTrappedAir", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_flipComputePotentialTrappedAir("",
|
|
"flipComputePotentialTrappedAir",
|
|
_W_7);
|
|
extern "C" {
|
|
void PbRegister_flipComputePotentialTrappedAir()
|
|
{
|
|
KEEP_UNUSED(_RP_flipComputePotentialTrappedAir);
|
|
}
|
|
}
|
|
|
|
// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead!
|
|
// computes kinetic energy potential for all fluid cells in &flags and saves it in &pot
|
|
|
|
struct knFlipComputePotentialKineticEnergy : public KernelBase {
|
|
knFlipComputePotentialKineticEnergy(Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid)
|
|
: KernelBase(&pot, 0),
|
|
pot(pot),
|
|
flags(flags),
|
|
v(v),
|
|
tauMin(tauMin),
|
|
tauMax(tauMax),
|
|
scaleFromManta(scaleFromManta),
|
|
itype(itype)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid) const
|
|
{
|
|
|
|
if (!(flags(i, j, k) & itype))
|
|
return;
|
|
|
|
const Vec3 &vi = scaleFromManta * v.getCentered(i, j, k); // scale to unit cube
|
|
Real ek =
|
|
Real(0.5) * 125 *
|
|
normSquare(
|
|
vi); // use arbitrary constant for mass, potential adjusts with thresholds anyways
|
|
pot(i, j, k) = (std::min(ek, tauMax) - std::min(ek, tauMin)) / (tauMax - tauMin);
|
|
}
|
|
inline Grid<Real> &getArg0()
|
|
{
|
|
return pot;
|
|
}
|
|
typedef Grid<Real> type0;
|
|
inline const FlagGrid &getArg1()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type1;
|
|
inline const MACGrid &getArg2()
|
|
{
|
|
return v;
|
|
}
|
|
typedef MACGrid type2;
|
|
inline const Real &getArg3()
|
|
{
|
|
return tauMin;
|
|
}
|
|
typedef Real type3;
|
|
inline const Real &getArg4()
|
|
{
|
|
return tauMax;
|
|
}
|
|
typedef Real type4;
|
|
inline const Real &getArg5()
|
|
{
|
|
return scaleFromManta;
|
|
}
|
|
typedef Real type5;
|
|
inline const int &getArg6()
|
|
{
|
|
return itype;
|
|
}
|
|
typedef int type6;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipComputePotentialKineticEnergy ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 0; j < _maxY; j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, pot, flags, v, tauMin, tauMax, scaleFromManta, itype);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, pot, flags, v, tauMin, tauMax, scaleFromManta, itype);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this);
|
|
}
|
|
Grid<Real> &pot;
|
|
const FlagGrid &flags;
|
|
const MACGrid &v;
|
|
const Real tauMin;
|
|
const Real tauMax;
|
|
const Real scaleFromManta;
|
|
const int itype;
|
|
};
|
|
|
|
void flipComputePotentialKineticEnergy(Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid)
|
|
{
|
|
pot.clear();
|
|
knFlipComputePotentialKineticEnergy(pot, flags, v, tauMin, tauMax, scaleFromManta, itype);
|
|
}
|
|
static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "flipComputePotentialKineticEnergy", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
Grid<Real> &pot = *_args.getPtr<Grid<Real>>("pot", 0, &_lock);
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock);
|
|
const MACGrid &v = *_args.getPtr<MACGrid>("v", 2, &_lock);
|
|
const Real tauMin = _args.get<Real>("tauMin", 3, &_lock);
|
|
const Real tauMax = _args.get<Real>("tauMax", 4, &_lock);
|
|
const Real scaleFromManta = _args.get<Real>("scaleFromManta", 5, &_lock);
|
|
const int itype = _args.getOpt<int>("itype", 6, FlagGrid::TypeFluid, &_lock);
|
|
_retval = getPyNone();
|
|
flipComputePotentialKineticEnergy(pot, flags, v, tauMin, tauMax, scaleFromManta, itype);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "flipComputePotentialKineticEnergy", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("flipComputePotentialKineticEnergy", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_flipComputePotentialKineticEnergy(
|
|
"", "flipComputePotentialKineticEnergy", _W_8);
|
|
extern "C" {
|
|
void PbRegister_flipComputePotentialKineticEnergy()
|
|
{
|
|
KEEP_UNUSED(_RP_flipComputePotentialKineticEnergy);
|
|
}
|
|
}
|
|
|
|
// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead!
|
|
// computes wave crest potential for all fluid cells in &flags and saves it in &pot
|
|
|
|
struct knFlipComputePotentialWaveCrest : public KernelBase {
|
|
knFlipComputePotentialWaveCrest(Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const int radius,
|
|
Grid<Vec3> &normal,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeFluid)
|
|
: KernelBase(&pot, 1),
|
|
pot(pot),
|
|
flags(flags),
|
|
v(v),
|
|
radius(radius),
|
|
normal(normal),
|
|
tauMin(tauMin),
|
|
tauMax(tauMax),
|
|
scaleFromManta(scaleFromManta),
|
|
itype(itype),
|
|
jtype(jtype)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const int radius,
|
|
Grid<Vec3> &normal,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeFluid) const
|
|
{
|
|
|
|
if (!(flags(i, j, k) & itype))
|
|
return;
|
|
|
|
const Vec3 &xi = scaleFromManta * Vec3(i, j, k); // scale to unit cube
|
|
const Vec3 &vi = scaleFromManta * v.getCentered(i, j, k);
|
|
const Vec3 &ni = normal(i, j, k);
|
|
Real kappa = 0;
|
|
for (IndexInt x = i - radius; x <= i + radius; x++) {
|
|
for (IndexInt y = j - radius; y <= j + radius; y++) {
|
|
for (IndexInt z = k - radius; z <= k + radius; z++) {
|
|
if ((x == i && y == j && z == k) || !(flags(x, y, z) & jtype))
|
|
continue;
|
|
const Vec3 &xj = scaleFromManta * Vec3(x, y, z); // scale to unit cube
|
|
const Vec3 &nj = normal(x, y, z);
|
|
const Vec3 xij = xi - xj;
|
|
if (dot(getNormalized(xij), ni) < 0) { // identifies wave crests
|
|
Real h = !pot.is3D() ?
|
|
1.414 * radius :
|
|
1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius for h,
|
|
// due to squared resp. cubic neighbor area
|
|
kappa += (1 - dot(ni, nj)) * (1 - norm(xij) / h);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dot(getNormalized(vi), ni) >= 0.6) { // avoid to mark boarders of the scene as wave crest
|
|
pot(i, j, k) = (std::min(kappa, tauMax) - std::min(kappa, tauMin)) / (tauMax - tauMin);
|
|
}
|
|
else {
|
|
pot(i, j, k) = Real(0);
|
|
}
|
|
}
|
|
inline Grid<Real> &getArg0()
|
|
{
|
|
return pot;
|
|
}
|
|
typedef Grid<Real> type0;
|
|
inline const FlagGrid &getArg1()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type1;
|
|
inline const MACGrid &getArg2()
|
|
{
|
|
return v;
|
|
}
|
|
typedef MACGrid type2;
|
|
inline const int &getArg3()
|
|
{
|
|
return radius;
|
|
}
|
|
typedef int type3;
|
|
inline Grid<Vec3> &getArg4()
|
|
{
|
|
return normal;
|
|
}
|
|
typedef Grid<Vec3> type4;
|
|
inline const Real &getArg5()
|
|
{
|
|
return tauMin;
|
|
}
|
|
typedef Real type5;
|
|
inline const Real &getArg6()
|
|
{
|
|
return tauMax;
|
|
}
|
|
typedef Real type6;
|
|
inline const Real &getArg7()
|
|
{
|
|
return scaleFromManta;
|
|
}
|
|
typedef Real type7;
|
|
inline const int &getArg8()
|
|
{
|
|
return itype;
|
|
}
|
|
typedef int type8;
|
|
inline const int &getArg9()
|
|
{
|
|
return jtype;
|
|
}
|
|
typedef int type9;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipComputePotentialWaveCrest ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i,
|
|
j,
|
|
k,
|
|
pot,
|
|
flags,
|
|
v,
|
|
radius,
|
|
normal,
|
|
tauMin,
|
|
tauMax,
|
|
scaleFromManta,
|
|
itype,
|
|
jtype);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, pot, flags, v, radius, normal, tauMin, tauMax, scaleFromManta, itype, jtype);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
Grid<Real> &pot;
|
|
const FlagGrid &flags;
|
|
const MACGrid &v;
|
|
const int radius;
|
|
Grid<Vec3> &normal;
|
|
const Real tauMin;
|
|
const Real tauMax;
|
|
const Real scaleFromManta;
|
|
const int itype;
|
|
const int jtype;
|
|
};
|
|
|
|
void flipComputePotentialWaveCrest(Grid<Real> &pot,
|
|
const FlagGrid &flags,
|
|
const MACGrid &v,
|
|
const int radius,
|
|
Grid<Vec3> &normal,
|
|
const Real tauMin,
|
|
const Real tauMax,
|
|
const Real scaleFromManta,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeFluid)
|
|
{
|
|
|
|
pot.clear();
|
|
knFlipComputePotentialWaveCrest(
|
|
pot, flags, v, radius, normal, tauMin, tauMax, scaleFromManta, itype, jtype);
|
|
}
|
|
static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "flipComputePotentialWaveCrest", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
Grid<Real> &pot = *_args.getPtr<Grid<Real>>("pot", 0, &_lock);
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock);
|
|
const MACGrid &v = *_args.getPtr<MACGrid>("v", 2, &_lock);
|
|
const int radius = _args.get<int>("radius", 3, &_lock);
|
|
Grid<Vec3> &normal = *_args.getPtr<Grid<Vec3>>("normal", 4, &_lock);
|
|
const Real tauMin = _args.get<Real>("tauMin", 5, &_lock);
|
|
const Real tauMax = _args.get<Real>("tauMax", 6, &_lock);
|
|
const Real scaleFromManta = _args.get<Real>("scaleFromManta", 7, &_lock);
|
|
const int itype = _args.getOpt<int>("itype", 8, FlagGrid::TypeFluid, &_lock);
|
|
const int jtype = _args.getOpt<int>("jtype", 9, FlagGrid::TypeFluid, &_lock);
|
|
_retval = getPyNone();
|
|
flipComputePotentialWaveCrest(
|
|
pot, flags, v, radius, normal, tauMin, tauMax, scaleFromManta, itype, jtype);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "flipComputePotentialWaveCrest", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("flipComputePotentialWaveCrest", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_flipComputePotentialWaveCrest("",
|
|
"flipComputePotentialWaveCrest",
|
|
_W_9);
|
|
extern "C" {
|
|
void PbRegister_flipComputePotentialWaveCrest()
|
|
{
|
|
KEEP_UNUSED(_RP_flipComputePotentialWaveCrest);
|
|
}
|
|
}
|
|
|
|
// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead!
|
|
// computes normal grid &normal as gradient of levelset &phi and normalizes it
|
|
|
|
struct knFlipComputeSurfaceNormals : public KernelBase {
|
|
knFlipComputeSurfaceNormals(Grid<Vec3> &normal, const Grid<Real> &phi)
|
|
: KernelBase(&normal, 0), normal(normal), phi(phi)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(IndexInt idx, Grid<Vec3> &normal, const Grid<Real> &phi) const
|
|
{
|
|
normal[idx] = getNormalized(normal[idx]);
|
|
}
|
|
inline Grid<Vec3> &getArg0()
|
|
{
|
|
return normal;
|
|
}
|
|
typedef Grid<Vec3> type0;
|
|
inline const Grid<Real> &getArg1()
|
|
{
|
|
return phi;
|
|
}
|
|
typedef Grid<Real> type1;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipComputeSurfaceNormals ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
|
|
op(idx, normal, phi);
|
|
}
|
|
void run()
|
|
{
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
|
|
}
|
|
Grid<Vec3> &normal;
|
|
const Grid<Real> φ
|
|
};
|
|
|
|
void flipComputeSurfaceNormals(Grid<Vec3> &normal, const Grid<Real> &phi)
|
|
{
|
|
GradientOp(normal, phi);
|
|
knFlipComputeSurfaceNormals(normal, phi);
|
|
}
|
|
static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "flipComputeSurfaceNormals", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
Grid<Vec3> &normal = *_args.getPtr<Grid<Vec3>>("normal", 0, &_lock);
|
|
const Grid<Real> &phi = *_args.getPtr<Grid<Real>>("phi", 1, &_lock);
|
|
_retval = getPyNone();
|
|
flipComputeSurfaceNormals(normal, phi);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "flipComputeSurfaceNormals", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("flipComputeSurfaceNormals", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_flipComputeSurfaceNormals("", "flipComputeSurfaceNormals", _W_10);
|
|
extern "C" {
|
|
void PbRegister_flipComputeSurfaceNormals()
|
|
{
|
|
KEEP_UNUSED(_RP_flipComputeSurfaceNormals);
|
|
}
|
|
}
|
|
|
|
// LEGACY METHOD! Use flipComputeSecondaryParticlePotentials instead!
|
|
// computes the neighbor ratio for every fluid cell in &flags as the number of fluid neighbors over
|
|
// the maximum possible number of fluid neighbors
|
|
|
|
struct knFlipUpdateNeighborRatio : public KernelBase {
|
|
knFlipUpdateNeighborRatio(const FlagGrid &flags,
|
|
Grid<Real> &neighborRatio,
|
|
const int radius,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeObstacle)
|
|
: KernelBase(&flags, 1),
|
|
flags(flags),
|
|
neighborRatio(neighborRatio),
|
|
radius(radius),
|
|
itype(itype),
|
|
jtype(jtype)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
const FlagGrid &flags,
|
|
Grid<Real> &neighborRatio,
|
|
const int radius,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeObstacle) const
|
|
{
|
|
|
|
if (!(flags(i, j, k) & itype))
|
|
return;
|
|
|
|
int countFluid = 0;
|
|
int countMaxFluid = 0;
|
|
for (IndexInt x = i - radius; x <= i + radius; x++) {
|
|
for (IndexInt y = j - radius; y <= j + radius; y++) {
|
|
for (IndexInt z = k - radius; z <= k + radius; z++) {
|
|
if ((x == i && y == j && z == k) || (flags(x, y, z) & jtype))
|
|
continue;
|
|
if (flags(x, y, z) & itype) {
|
|
countFluid++;
|
|
countMaxFluid++;
|
|
}
|
|
else {
|
|
countMaxFluid++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
neighborRatio(i, j, k) = float(countFluid) / float(countMaxFluid);
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline Grid<Real> &getArg1()
|
|
{
|
|
return neighborRatio;
|
|
}
|
|
typedef Grid<Real> type1;
|
|
inline const int &getArg2()
|
|
{
|
|
return radius;
|
|
}
|
|
typedef int type2;
|
|
inline const int &getArg3()
|
|
{
|
|
return itype;
|
|
}
|
|
typedef int type3;
|
|
inline const int &getArg4()
|
|
{
|
|
return jtype;
|
|
}
|
|
typedef int type4;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel knFlipUpdateNeighborRatio ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, neighborRatio, radius, itype, jtype);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, neighborRatio, radius, itype, jtype);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
Grid<Real> &neighborRatio;
|
|
const int radius;
|
|
const int itype;
|
|
const int jtype;
|
|
};
|
|
|
|
void flipUpdateNeighborRatio(const FlagGrid &flags,
|
|
Grid<Real> &neighborRatio,
|
|
const int radius,
|
|
const int itype = FlagGrid::TypeFluid,
|
|
const int jtype = FlagGrid::TypeObstacle)
|
|
{
|
|
|
|
neighborRatio.clear();
|
|
knFlipUpdateNeighborRatio(flags, neighborRatio, radius, itype, jtype);
|
|
}
|
|
static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "flipUpdateNeighborRatio", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
Grid<Real> &neighborRatio = *_args.getPtr<Grid<Real>>("neighborRatio", 1, &_lock);
|
|
const int radius = _args.get<int>("radius", 2, &_lock);
|
|
const int itype = _args.getOpt<int>("itype", 3, FlagGrid::TypeFluid, &_lock);
|
|
const int jtype = _args.getOpt<int>("jtype", 4, FlagGrid::TypeObstacle, &_lock);
|
|
_retval = getPyNone();
|
|
flipUpdateNeighborRatio(flags, neighborRatio, radius, itype, jtype);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "flipUpdateNeighborRatio", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("flipUpdateNeighborRatio", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_flipUpdateNeighborRatio("", "flipUpdateNeighborRatio", _W_11);
|
|
extern "C" {
|
|
void PbRegister_flipUpdateNeighborRatio()
|
|
{
|
|
KEEP_UNUSED(_RP_flipUpdateNeighborRatio);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
// Legacy Methods (still useful for debugging)
|
|
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
#pragma endregion
|
|
|
|
} // namespace Manta
|