Mainly updated the Mantaflow version. It includes the new viscosity solver plugin based on the method from 'Accurate Viscous Free Surfaces for Buckling, Coiling, and Rotating Liquids' (Batty & Bridson). In the UI, this update adds a new 'Viscosity' section to the fluid modifier UI (liquid domains only). For now, there is a single 'strength' value to control the viscosity of liquids.
1700 lines
51 KiB
C++
1700 lines
51 KiB
C++
|
|
|
|
// DO NOT EDIT !
|
|
// This file is generated using the MantaFlow preprocessor (prep generate).
|
|
|
|
/******************************************************************************
|
|
*
|
|
* MantaFlow fluid solver framework
|
|
* Copyright 2011 Tobias Pfaff, Nils Thuerey
|
|
*
|
|
* This program is free software, distributed under the terms of the
|
|
* GNU General Public License (GPL)
|
|
* http://www.gnu.org/licenses
|
|
*
|
|
* Set boundary conditions, gravity
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include "vectorbase.h"
|
|
#include "grid.h"
|
|
#include "commonkernels.h"
|
|
#include "particle.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace Manta {
|
|
|
|
//! add constant force between fl/fl and fl/em cells
|
|
struct KnApplyForceField : public KernelBase {
|
|
KnApplyForceField(const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
const Grid<Vec3> &force,
|
|
const Grid<Real> *include,
|
|
bool additive,
|
|
bool isMAC)
|
|
: KernelBase(&flags, 1),
|
|
flags(flags),
|
|
vel(vel),
|
|
force(force),
|
|
include(include),
|
|
additive(additive),
|
|
isMAC(isMAC)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
const Grid<Vec3> &force,
|
|
const Grid<Real> *include,
|
|
bool additive,
|
|
bool isMAC) const
|
|
{
|
|
bool curFluid = flags.isFluid(i, j, k);
|
|
bool curEmpty = flags.isEmpty(i, j, k);
|
|
if (!curFluid && !curEmpty)
|
|
return;
|
|
if (include && ((*include)(i, j, k) > 0.))
|
|
return;
|
|
|
|
Real forceX = (isMAC) ? force(i, j, k).x : 0.5 * (force(i - 1, j, k).x + force(i, j, k).x);
|
|
Real forceY = (isMAC) ? force(i, j, k).y : 0.5 * (force(i, j - 1, k).y + force(i, j, k).y);
|
|
|
|
Real forceZ = 0.;
|
|
if (vel.is3D())
|
|
forceZ = (isMAC) ? force(i, j, k).z : 0.5 * (force(i, j, k - 1).z + force(i, j, k).z);
|
|
|
|
if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k)))
|
|
vel(i, j, k).x = (additive) ? vel(i, j, k).x + forceX : forceX;
|
|
if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k)))
|
|
vel(i, j, k).y = (additive) ? vel(i, j, k).y + forceY : forceY;
|
|
if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1))))
|
|
vel(i, j, k).z = (additive) ? vel(i, j, k).z + forceZ : forceZ;
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline MACGrid &getArg1()
|
|
{
|
|
return vel;
|
|
}
|
|
typedef MACGrid type1;
|
|
inline const Grid<Vec3> &getArg2()
|
|
{
|
|
return force;
|
|
}
|
|
typedef Grid<Vec3> type2;
|
|
inline const Grid<Real> *getArg3()
|
|
{
|
|
return include;
|
|
}
|
|
typedef Grid<Real> type3;
|
|
inline bool &getArg4()
|
|
{
|
|
return additive;
|
|
}
|
|
typedef bool type4;
|
|
inline bool &getArg5()
|
|
{
|
|
return isMAC;
|
|
}
|
|
typedef bool type5;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnApplyForceField ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, force, include, additive, isMAC);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, force, include, additive, isMAC);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
MACGrid &vel;
|
|
const Grid<Vec3> &force;
|
|
const Grid<Real> *include;
|
|
bool additive;
|
|
bool isMAC;
|
|
};
|
|
|
|
//! add constant force between fl/fl and fl/em cells
|
|
struct KnApplyForce : public KernelBase {
|
|
KnApplyForce(
|
|
const FlagGrid &flags, MACGrid &vel, Vec3 force, const Grid<Real> *exclude, bool additive)
|
|
: KernelBase(&flags, 1),
|
|
flags(flags),
|
|
vel(vel),
|
|
force(force),
|
|
exclude(exclude),
|
|
additive(additive)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
Vec3 force,
|
|
const Grid<Real> *exclude,
|
|
bool additive) const
|
|
{
|
|
bool curFluid = flags.isFluid(i, j, k);
|
|
bool curEmpty = flags.isEmpty(i, j, k);
|
|
if (!curFluid && !curEmpty)
|
|
return;
|
|
if (exclude && ((*exclude)(i, j, k) < 0.))
|
|
return;
|
|
|
|
if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k)))
|
|
vel(i, j, k).x = (additive) ? vel(i, j, k).x + force.x : force.x;
|
|
if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k)))
|
|
vel(i, j, k).y = (additive) ? vel(i, j, k).y + force.y : force.y;
|
|
if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1))))
|
|
vel(i, j, k).z = (additive) ? vel(i, j, k).z + force.z : force.z;
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline MACGrid &getArg1()
|
|
{
|
|
return vel;
|
|
}
|
|
typedef MACGrid type1;
|
|
inline Vec3 &getArg2()
|
|
{
|
|
return force;
|
|
}
|
|
typedef Vec3 type2;
|
|
inline const Grid<Real> *getArg3()
|
|
{
|
|
return exclude;
|
|
}
|
|
typedef Grid<Real> type3;
|
|
inline bool &getArg4()
|
|
{
|
|
return additive;
|
|
}
|
|
typedef bool type4;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnApplyForce ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, force, exclude, additive);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, force, exclude, additive);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
MACGrid &vel;
|
|
Vec3 force;
|
|
const Grid<Real> *exclude;
|
|
bool additive;
|
|
};
|
|
|
|
//! add gravity forces to all fluid cells, optionally adapts to different grid sizes automatically
|
|
void addGravity(const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
Vec3 gravity,
|
|
const Grid<Real> *exclude = nullptr,
|
|
bool scale = true)
|
|
{
|
|
float gridScale = (scale) ? flags.getDx() : 1;
|
|
Vec3 f = gravity * flags.getParent()->getDt() / gridScale;
|
|
KnApplyForce(flags, vel, f, exclude, true);
|
|
}
|
|
static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "addGravity", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
|
|
Vec3 gravity = _args.get<Vec3>("gravity", 2, &_lock);
|
|
const Grid<Real> *exclude = _args.getPtrOpt<Grid<Real>>("exclude", 3, nullptr, &_lock);
|
|
bool scale = _args.getOpt<bool>("scale", 4, true, &_lock);
|
|
_retval = getPyNone();
|
|
addGravity(flags, vel, gravity, exclude, scale);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "addGravity", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("addGravity", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_addGravity("", "addGravity", _W_0);
|
|
extern "C" {
|
|
void PbRegister_addGravity()
|
|
{
|
|
KEEP_UNUSED(_RP_addGravity);
|
|
}
|
|
}
|
|
|
|
//! Deprecated: use addGravity(scale=false) instead
|
|
void addGravityNoScale(const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
const Vec3 &gravity,
|
|
const Grid<Real> *exclude = nullptr)
|
|
{
|
|
addGravity(flags, vel, gravity, exclude, false);
|
|
}
|
|
static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "addGravityNoScale", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
|
|
const Vec3 &gravity = _args.get<Vec3>("gravity", 2, &_lock);
|
|
const Grid<Real> *exclude = _args.getPtrOpt<Grid<Real>>("exclude", 3, nullptr, &_lock);
|
|
_retval = getPyNone();
|
|
addGravityNoScale(flags, vel, gravity, exclude);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "addGravityNoScale", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("addGravityNoScale", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_addGravityNoScale("", "addGravityNoScale", _W_1);
|
|
extern "C" {
|
|
void PbRegister_addGravityNoScale()
|
|
{
|
|
KEEP_UNUSED(_RP_addGravityNoScale);
|
|
}
|
|
}
|
|
|
|
//! kernel to add Buoyancy force
|
|
struct KnAddBuoyancy : public KernelBase {
|
|
KnAddBuoyancy(const FlagGrid &flags, const Grid<Real> &factor, MACGrid &vel, Vec3 strength)
|
|
: KernelBase(&flags, 1), flags(flags), factor(factor), vel(vel), strength(strength)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
const FlagGrid &flags,
|
|
const Grid<Real> &factor,
|
|
MACGrid &vel,
|
|
Vec3 strength) const
|
|
{
|
|
if (!flags.isFluid(i, j, k))
|
|
return;
|
|
if (flags.isFluid(i - 1, j, k))
|
|
vel(i, j, k).x += (0.5 * strength.x) * (factor(i, j, k) + factor(i - 1, j, k));
|
|
if (flags.isFluid(i, j - 1, k))
|
|
vel(i, j, k).y += (0.5 * strength.y) * (factor(i, j, k) + factor(i, j - 1, k));
|
|
if (vel.is3D() && flags.isFluid(i, j, k - 1))
|
|
vel(i, j, k).z += (0.5 * strength.z) * (factor(i, j, k) + factor(i, j, k - 1));
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline const Grid<Real> &getArg1()
|
|
{
|
|
return factor;
|
|
}
|
|
typedef Grid<Real> type1;
|
|
inline MACGrid &getArg2()
|
|
{
|
|
return vel;
|
|
}
|
|
typedef MACGrid type2;
|
|
inline Vec3 &getArg3()
|
|
{
|
|
return strength;
|
|
}
|
|
typedef Vec3 type3;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnAddBuoyancy ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, factor, vel, strength);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, factor, vel, strength);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
const Grid<Real> &factor;
|
|
MACGrid &vel;
|
|
Vec3 strength;
|
|
};
|
|
|
|
//! add Buoyancy force based on factor (e.g. smoke density), optionally adapts to different grid
|
|
//! sizes automatically
|
|
void addBuoyancy(const FlagGrid &flags,
|
|
const Grid<Real> &density,
|
|
MACGrid &vel,
|
|
Vec3 gravity,
|
|
Real coefficient = 1.,
|
|
bool scale = true)
|
|
{
|
|
float gridScale = (scale) ? flags.getDx() : 1;
|
|
Vec3 f = -gravity * flags.getParent()->getDt() / gridScale * coefficient;
|
|
KnAddBuoyancy(flags, density, vel, f);
|
|
}
|
|
static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "addBuoyancy", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
const Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock);
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 2, &_lock);
|
|
Vec3 gravity = _args.get<Vec3>("gravity", 3, &_lock);
|
|
Real coefficient = _args.getOpt<Real>("coefficient", 4, 1., &_lock);
|
|
bool scale = _args.getOpt<bool>("scale", 5, true, &_lock);
|
|
_retval = getPyNone();
|
|
addBuoyancy(flags, density, vel, gravity, coefficient, scale);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "addBuoyancy", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("addBuoyancy", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_addBuoyancy("", "addBuoyancy", _W_2);
|
|
extern "C" {
|
|
void PbRegister_addBuoyancy()
|
|
{
|
|
KEEP_UNUSED(_RP_addBuoyancy);
|
|
}
|
|
}
|
|
|
|
// inflow / outflow boundaries
|
|
|
|
//! helper to parse openbounds string [xXyYzZ] , convert to vec3
|
|
inline void convertDescToVec(const string &desc, Vector3D<bool> &lo, Vector3D<bool> &up)
|
|
{
|
|
for (size_t i = 0; i < desc.size(); i++) {
|
|
if (desc[i] == 'x')
|
|
lo.x = true;
|
|
else if (desc[i] == 'y')
|
|
lo.y = true;
|
|
else if (desc[i] == 'z')
|
|
lo.z = true;
|
|
else if (desc[i] == 'X')
|
|
up.x = true;
|
|
else if (desc[i] == 'Y')
|
|
up.y = true;
|
|
else if (desc[i] == 'Z')
|
|
up.z = true;
|
|
else
|
|
errMsg("invalid character in boundary description string. Only [xyzXYZ] allowed.");
|
|
}
|
|
}
|
|
|
|
//! add empty and outflow flag to cells of open boundaries
|
|
void setOpenBound(FlagGrid &flags,
|
|
int bWidth,
|
|
string openBound = "",
|
|
int type = FlagGrid::TypeOutflow | FlagGrid::TypeEmpty)
|
|
{
|
|
if (openBound == "")
|
|
return;
|
|
Vector3D<bool> lo, up;
|
|
convertDescToVec(openBound, lo, up);
|
|
|
|
FOR_IJK(flags)
|
|
{
|
|
bool loX = lo.x && i <= bWidth; // a cell which belongs to the lower x open bound
|
|
bool loY = lo.y && j <= bWidth;
|
|
bool upX = up.x && i >= flags.getSizeX() - bWidth -
|
|
1; // a cell which belongs to the upper x open bound
|
|
bool upY = up.y && j >= flags.getSizeY() - bWidth - 1;
|
|
bool innerI = i > bWidth &&
|
|
i < flags.getSizeX() - bWidth -
|
|
1; // a cell which does not belong to the lower or upper x bound
|
|
bool innerJ = j > bWidth && j < flags.getSizeY() - bWidth - 1;
|
|
|
|
// when setting boundaries to open: don't set shared part of wall to empty if neighboring wall
|
|
// is not open
|
|
if ((!flags.is3D()) && (loX || upX || loY || upY)) {
|
|
if ((loX || upX || innerI) && (loY || upY || innerJ) && flags.isObstacle(i, j, k))
|
|
flags(i, j, k) = type;
|
|
}
|
|
else {
|
|
bool loZ = lo.z && k <= bWidth; // a cell which belongs to the lower z open bound
|
|
bool upZ = up.z && k >= flags.getSizeZ() - bWidth -
|
|
1; // a cell which belongs to the upper z open bound
|
|
bool innerK = k > bWidth &&
|
|
k < flags.getSizeZ() - bWidth -
|
|
1; // a cell which does not belong to the lower or upper z bound
|
|
if (loX || upX || loY || upY || loZ || upZ) {
|
|
if ((loX || upX || innerI) && (loY || upY || innerJ) && (loZ || upZ || innerK) &&
|
|
flags.isObstacle(i, j, k))
|
|
flags(i, j, k) = type;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "setOpenBound", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
int bWidth = _args.get<int>("bWidth", 1, &_lock);
|
|
string openBound = _args.getOpt<string>("openBound", 2, "", &_lock);
|
|
int type = _args.getOpt<int>("type", 3, FlagGrid::TypeOutflow | FlagGrid::TypeEmpty, &_lock);
|
|
_retval = getPyNone();
|
|
setOpenBound(flags, bWidth, openBound, type);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "setOpenBound", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("setOpenBound", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_setOpenBound("", "setOpenBound", _W_3);
|
|
extern "C" {
|
|
void PbRegister_setOpenBound()
|
|
{
|
|
KEEP_UNUSED(_RP_setOpenBound);
|
|
}
|
|
}
|
|
|
|
//! delete fluid and ensure empty flag in outflow cells, delete particles and density and set phi
|
|
//! to 0.5
|
|
void resetOutflow(FlagGrid &flags,
|
|
Grid<Real> *phi = nullptr,
|
|
BasicParticleSystem *parts = nullptr,
|
|
Grid<Real> *real = nullptr,
|
|
Grid<int> *index = nullptr,
|
|
ParticleIndexSystem *indexSys = nullptr)
|
|
{
|
|
// check if phi and parts -> pindex and gpi already created -> access particles from cell index,
|
|
// avoid extra looping over particles
|
|
if (parts && (!index || !indexSys)) {
|
|
if (phi)
|
|
debMsg(
|
|
"resetOpenBound for phi and particles, but missing index and indexSys for enhanced "
|
|
"particle access!",
|
|
1);
|
|
for (int idx = 0; idx < (int)parts->size(); idx++)
|
|
if (parts->isActive(idx) && flags.isInBounds(parts->getPos(idx)) &&
|
|
flags.isOutflow(parts->getPos(idx)))
|
|
parts->kill(idx);
|
|
}
|
|
FOR_IJK(flags)
|
|
{
|
|
if (flags.isOutflow(i, j, k)) {
|
|
flags(i, j, k) = (flags(i, j, k) | FlagGrid::TypeEmpty) &
|
|
~FlagGrid::TypeFluid; // make sure there is not fluid flag set and to reset
|
|
// the empty flag
|
|
// the particles in a cell i,j,k are particles[index(i,j,k)] to particles[index(i+1,j,k)-1]
|
|
if (parts && index && indexSys) {
|
|
int isysIdxS = index->index(i, j, k);
|
|
int pStart = (*index)(isysIdxS), pEnd = 0;
|
|
if (flags.isInBounds(isysIdxS + 1))
|
|
pEnd = (*index)(isysIdxS + 1);
|
|
else
|
|
pEnd = indexSys->size();
|
|
// now loop over particles in cell
|
|
for (int p = pStart; p < pEnd; ++p) {
|
|
int psrc = (*indexSys)[p].sourceIndex;
|
|
if (parts->isActive(psrc) && flags.isInBounds(parts->getPos(psrc)))
|
|
parts->kill(psrc);
|
|
}
|
|
}
|
|
if (phi)
|
|
(*phi)(i, j, k) = 0.5;
|
|
if (real)
|
|
(*real)(i, j, k) = 0;
|
|
}
|
|
}
|
|
if (parts)
|
|
parts->doCompress();
|
|
}
|
|
static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "resetOutflow", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
Grid<Real> *phi = _args.getPtrOpt<Grid<Real>>("phi", 1, nullptr, &_lock);
|
|
BasicParticleSystem *parts = _args.getPtrOpt<BasicParticleSystem>(
|
|
"parts", 2, nullptr, &_lock);
|
|
Grid<Real> *real = _args.getPtrOpt<Grid<Real>>("real", 3, nullptr, &_lock);
|
|
Grid<int> *index = _args.getPtrOpt<Grid<int>>("index", 4, nullptr, &_lock);
|
|
ParticleIndexSystem *indexSys = _args.getPtrOpt<ParticleIndexSystem>(
|
|
"indexSys", 5, nullptr, &_lock);
|
|
_retval = getPyNone();
|
|
resetOutflow(flags, phi, parts, real, index, indexSys);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "resetOutflow", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("resetOutflow", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_resetOutflow("", "resetOutflow", _W_4);
|
|
extern "C" {
|
|
void PbRegister_resetOutflow()
|
|
{
|
|
KEEP_UNUSED(_RP_resetOutflow);
|
|
}
|
|
}
|
|
|
|
//! enforce a constant inflow/outflow at the grid boundaries
|
|
struct KnSetInflow : public KernelBase {
|
|
KnSetInflow(MACGrid &vel, int dim, int p0, const Vec3 &val)
|
|
: KernelBase(&vel, 0), vel(vel), dim(dim), p0(p0), val(val)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i, int j, int k, MACGrid &vel, int dim, int p0, const Vec3 &val) const
|
|
{
|
|
Vec3i p(i, j, k);
|
|
if (p[dim] == p0 || p[dim] == p0 + 1)
|
|
vel(i, j, k) = val;
|
|
}
|
|
inline MACGrid &getArg0()
|
|
{
|
|
return vel;
|
|
}
|
|
typedef MACGrid type0;
|
|
inline int &getArg1()
|
|
{
|
|
return dim;
|
|
}
|
|
typedef int type1;
|
|
inline int &getArg2()
|
|
{
|
|
return p0;
|
|
}
|
|
typedef int type2;
|
|
inline const Vec3 &getArg3()
|
|
{
|
|
return val;
|
|
}
|
|
typedef Vec3 type3;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnSetInflow ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 0; j < _maxY; j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, vel, dim, p0, val);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, vel, dim, p0, val);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this);
|
|
}
|
|
MACGrid &vel;
|
|
int dim;
|
|
int p0;
|
|
const Vec3 &val;
|
|
};
|
|
|
|
//! enforce a constant inflow/outflow at the grid boundaries
|
|
void setInflowBcs(MACGrid &vel, string dir, Vec3 value)
|
|
{
|
|
for (size_t i = 0; i < dir.size(); i++) {
|
|
if (dir[i] >= 'x' && dir[i] <= 'z') {
|
|
int dim = dir[i] - 'x';
|
|
KnSetInflow(vel, dim, 0, value);
|
|
}
|
|
else if (dir[i] >= 'X' && dir[i] <= 'Z') {
|
|
int dim = dir[i] - 'X';
|
|
KnSetInflow(vel, dim, vel.getSize()[dim] - 1, value);
|
|
}
|
|
else
|
|
errMsg("invalid character in direction string. Only [xyzXYZ] allowed.");
|
|
}
|
|
}
|
|
static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "setInflowBcs", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock);
|
|
string dir = _args.get<string>("dir", 1, &_lock);
|
|
Vec3 value = _args.get<Vec3>("value", 2, &_lock);
|
|
_retval = getPyNone();
|
|
setInflowBcs(vel, dir, value);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "setInflowBcs", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("setInflowBcs", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_setInflowBcs("", "setInflowBcs", _W_5);
|
|
extern "C" {
|
|
void PbRegister_setInflowBcs()
|
|
{
|
|
KEEP_UNUSED(_RP_setInflowBcs);
|
|
}
|
|
}
|
|
|
|
// set obstacle boundary conditions
|
|
|
|
//! set no-stick wall boundary condition between ob/fl and ob/ob cells
|
|
struct KnSetWallBcs : public KernelBase {
|
|
KnSetWallBcs(const FlagGrid &flags, MACGrid &vel, const MACGrid *obvel)
|
|
: KernelBase(&flags, 0), flags(flags), vel(vel), obvel(obvel)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(
|
|
int i, int j, int k, const FlagGrid &flags, MACGrid &vel, const MACGrid *obvel) const
|
|
{
|
|
|
|
bool curFluid = flags.isFluid(i, j, k);
|
|
bool curObs = flags.isObstacle(i, j, k);
|
|
Vec3 bcsVel(0., 0., 0.);
|
|
if (!curFluid && !curObs)
|
|
return;
|
|
|
|
if (obvel) {
|
|
bcsVel.x = (*obvel)(i, j, k).x;
|
|
bcsVel.y = (*obvel)(i, j, k).y;
|
|
if ((*obvel).is3D())
|
|
bcsVel.z = (*obvel)(i, j, k).z;
|
|
}
|
|
|
|
// we use i>0 instead of bnd=1 to check outer wall
|
|
if (i > 0 && flags.isObstacle(i - 1, j, k))
|
|
vel(i, j, k).x = bcsVel.x;
|
|
if (i > 0 && curObs && flags.isFluid(i - 1, j, k))
|
|
vel(i, j, k).x = bcsVel.x;
|
|
if (j > 0 && flags.isObstacle(i, j - 1, k))
|
|
vel(i, j, k).y = bcsVel.y;
|
|
if (j > 0 && curObs && flags.isFluid(i, j - 1, k))
|
|
vel(i, j, k).y = bcsVel.y;
|
|
|
|
if (!vel.is3D()) {
|
|
vel(i, j, k).z = 0;
|
|
}
|
|
else {
|
|
if (k > 0 && flags.isObstacle(i, j, k - 1))
|
|
vel(i, j, k).z = bcsVel.z;
|
|
if (k > 0 && curObs && flags.isFluid(i, j, k - 1))
|
|
vel(i, j, k).z = bcsVel.z;
|
|
}
|
|
|
|
if (curFluid) {
|
|
if ((i > 0 && flags.isStick(i - 1, j, k)) ||
|
|
(i < flags.getSizeX() - 1 && flags.isStick(i + 1, j, k)))
|
|
vel(i, j, k).y = vel(i, j, k).z = 0;
|
|
if ((j > 0 && flags.isStick(i, j - 1, k)) ||
|
|
(j < flags.getSizeY() - 1 && flags.isStick(i, j + 1, k)))
|
|
vel(i, j, k).x = vel(i, j, k).z = 0;
|
|
if (vel.is3D() && ((k > 0 && flags.isStick(i, j, k - 1)) ||
|
|
(k < flags.getSizeZ() - 1 && flags.isStick(i, j, k + 1))))
|
|
vel(i, j, k).x = vel(i, j, k).y = 0;
|
|
}
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline MACGrid &getArg1()
|
|
{
|
|
return vel;
|
|
}
|
|
typedef MACGrid type1;
|
|
inline const MACGrid *getArg2()
|
|
{
|
|
return obvel;
|
|
}
|
|
typedef MACGrid type2;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnSetWallBcs ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 0; j < _maxY; j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, obvel);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, obvel);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
MACGrid &vel;
|
|
const MACGrid *obvel;
|
|
};
|
|
|
|
//! set wall BCs for fill fraction mode, note - only needs obstacle SDF
|
|
|
|
struct KnSetWallBcsFrac : public KernelBase {
|
|
KnSetWallBcsFrac(const FlagGrid &flags,
|
|
const MACGrid &vel,
|
|
MACGrid &velTarget,
|
|
const MACGrid *obvel,
|
|
const Grid<Real> *phiObs,
|
|
const int &boundaryWidth = 0)
|
|
: KernelBase(&flags, 0),
|
|
flags(flags),
|
|
vel(vel),
|
|
velTarget(velTarget),
|
|
obvel(obvel),
|
|
phiObs(phiObs),
|
|
boundaryWidth(boundaryWidth)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
const FlagGrid &flags,
|
|
const MACGrid &vel,
|
|
MACGrid &velTarget,
|
|
const MACGrid *obvel,
|
|
const Grid<Real> *phiObs,
|
|
const int &boundaryWidth = 0) const
|
|
{
|
|
bool curFluid = flags.isFluid(i, j, k);
|
|
bool curObs = flags.isObstacle(i, j, k);
|
|
velTarget(i, j, k) = vel(i, j, k);
|
|
if (!curFluid && !curObs)
|
|
return;
|
|
|
|
// zero normal component in all obstacle regions
|
|
if (flags.isInBounds(Vec3i(i, j, k), 1)) {
|
|
|
|
if (curObs | flags.isObstacle(i - 1, j, k)) {
|
|
Vec3 dphi(0., 0., 0.);
|
|
const Real tmp1 = (phiObs->get(i, j, k) + phiObs->get(i - 1, j, k)) * .5;
|
|
Real tmp2 = (phiObs->get(i, j + 1, k) + phiObs->get(i - 1, j + 1, k)) * .5;
|
|
Real phi1 = (tmp1 + tmp2) * .5;
|
|
tmp2 = (phiObs->get(i, j - 1, k) + phiObs->get(i - 1, j - 1, k)) * .5;
|
|
Real phi2 = (tmp1 + tmp2) * .5;
|
|
|
|
dphi.x = phiObs->get(i, j, k) - phiObs->get(i - 1, j, k);
|
|
dphi.y = phi1 - phi2;
|
|
|
|
if (phiObs->is3D()) {
|
|
tmp2 = (phiObs->get(i, j, k + 1) + phiObs->get(i - 1, j, k + 1)) * .5;
|
|
phi1 = (tmp1 + tmp2) * .5;
|
|
tmp2 = (phiObs->get(i, j, k - 1) + phiObs->get(i - 1, j, k - 1)) * .5;
|
|
phi2 = (tmp1 + tmp2) * .5;
|
|
dphi.z = phi1 - phi2;
|
|
}
|
|
|
|
normalize(dphi);
|
|
Vec3 velMAC = vel.getAtMACX(i, j, k);
|
|
velTarget(i, j, k).x = velMAC.x - dot(dphi, velMAC) * dphi.x;
|
|
}
|
|
|
|
if (curObs | flags.isObstacle(i, j - 1, k)) {
|
|
Vec3 dphi(0., 0., 0.);
|
|
const Real tmp1 = (phiObs->get(i, j, k) + phiObs->get(i, j - 1, k)) * .5;
|
|
Real tmp2 = (phiObs->get(i + 1, j, k) + phiObs->get(i + 1, j - 1, k)) * .5;
|
|
Real phi1 = (tmp1 + tmp2) * .5;
|
|
tmp2 = (phiObs->get(i - 1, j, k) + phiObs->get(i - 1, j - 1, k)) * .5;
|
|
Real phi2 = (tmp1 + tmp2) * .5;
|
|
|
|
dphi.x = phi1 - phi2;
|
|
dphi.y = phiObs->get(i, j, k) - phiObs->get(i, j - 1, k);
|
|
if (phiObs->is3D()) {
|
|
tmp2 = (phiObs->get(i, j, k + 1) + phiObs->get(i, j - 1, k + 1)) * .5;
|
|
phi1 = (tmp1 + tmp2) * .5;
|
|
tmp2 = (phiObs->get(i, j, k - 1) + phiObs->get(i, j - 1, k - 1)) * .5;
|
|
phi2 = (tmp1 + tmp2) * .5;
|
|
dphi.z = phi1 - phi2;
|
|
}
|
|
|
|
normalize(dphi);
|
|
Vec3 velMAC = vel.getAtMACY(i, j, k);
|
|
velTarget(i, j, k).y = velMAC.y - dot(dphi, velMAC) * dphi.y;
|
|
}
|
|
|
|
if (phiObs->is3D() && (curObs | flags.isObstacle(i, j, k - 1))) {
|
|
Vec3 dphi(0., 0., 0.);
|
|
const Real tmp1 = (phiObs->get(i, j, k) + phiObs->get(i, j, k - 1)) * .5;
|
|
|
|
Real tmp2;
|
|
tmp2 = (phiObs->get(i + 1, j, k) + phiObs->get(i + 1, j, k - 1)) * .5;
|
|
Real phi1 = (tmp1 + tmp2) * .5;
|
|
tmp2 = (phiObs->get(i - 1, j, k) + phiObs->get(i - 1, j, k - 1)) * .5;
|
|
Real phi2 = (tmp1 + tmp2) * .5;
|
|
dphi.x = phi1 - phi2;
|
|
|
|
tmp2 = (phiObs->get(i, j + 1, k) + phiObs->get(i, j + 1, k - 1)) * .5;
|
|
phi1 = (tmp1 + tmp2) * .5;
|
|
tmp2 = (phiObs->get(i, j - 1, k) + phiObs->get(i, j - 1, k - 1)) * .5;
|
|
phi2 = (tmp1 + tmp2) * .5;
|
|
dphi.y = phi1 - phi2;
|
|
|
|
dphi.z = phiObs->get(i, j, k) - phiObs->get(i, j, k - 1);
|
|
|
|
normalize(dphi);
|
|
Vec3 velMAC = vel.getAtMACZ(i, j, k);
|
|
velTarget(i, j, k).z = velMAC.z - dot(dphi, velMAC) * dphi.z;
|
|
}
|
|
} // not at boundary
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline const MACGrid &getArg1()
|
|
{
|
|
return vel;
|
|
}
|
|
typedef MACGrid type1;
|
|
inline MACGrid &getArg2()
|
|
{
|
|
return velTarget;
|
|
}
|
|
typedef MACGrid type2;
|
|
inline const MACGrid *getArg3()
|
|
{
|
|
return obvel;
|
|
}
|
|
typedef MACGrid type3;
|
|
inline const Grid<Real> *getArg4()
|
|
{
|
|
return phiObs;
|
|
}
|
|
typedef Grid<Real> type4;
|
|
inline const int &getArg5()
|
|
{
|
|
return boundaryWidth;
|
|
}
|
|
typedef int type5;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnSetWallBcsFrac ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 0; j < _maxY; j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, velTarget, obvel, phiObs, boundaryWidth);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, velTarget, obvel, phiObs, boundaryWidth);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
const MACGrid &vel;
|
|
MACGrid &velTarget;
|
|
const MACGrid *obvel;
|
|
const Grid<Real> *phiObs;
|
|
const int &boundaryWidth;
|
|
};
|
|
|
|
//! set zero normal velocity boundary condition on walls
|
|
// (optionally with second order accuracy using the obstacle SDF , fractions grid currently not
|
|
// needed)
|
|
void setWallBcs(const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
const MACGrid *obvel = nullptr,
|
|
const MACGrid *fractions = nullptr,
|
|
const Grid<Real> *phiObs = nullptr,
|
|
int boundaryWidth = 0)
|
|
{
|
|
if (!phiObs || !fractions) {
|
|
KnSetWallBcs(flags, vel, obvel);
|
|
}
|
|
else {
|
|
MACGrid tmpvel(vel.getParent());
|
|
KnSetWallBcsFrac(flags, vel, tmpvel, obvel, phiObs, boundaryWidth);
|
|
vel.swap(tmpvel);
|
|
}
|
|
}
|
|
static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "setWallBcs", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
|
|
const MACGrid *obvel = _args.getPtrOpt<MACGrid>("obvel", 2, nullptr, &_lock);
|
|
const MACGrid *fractions = _args.getPtrOpt<MACGrid>("fractions", 3, nullptr, &_lock);
|
|
const Grid<Real> *phiObs = _args.getPtrOpt<Grid<Real>>("phiObs", 4, nullptr, &_lock);
|
|
int boundaryWidth = _args.getOpt<int>("boundaryWidth", 5, 0, &_lock);
|
|
_retval = getPyNone();
|
|
setWallBcs(flags, vel, obvel, fractions, phiObs, boundaryWidth);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "setWallBcs", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("setWallBcs", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_setWallBcs("", "setWallBcs", _W_6);
|
|
extern "C" {
|
|
void PbRegister_setWallBcs()
|
|
{
|
|
KEEP_UNUSED(_RP_setWallBcs);
|
|
}
|
|
}
|
|
|
|
//! add Forces between fl/fl and fl/em cells (interpolate cell centered forces to MAC grid)
|
|
struct KnAddForceIfLower : public KernelBase {
|
|
KnAddForceIfLower(const FlagGrid &flags, MACGrid &vel, const Grid<Vec3> &force)
|
|
: KernelBase(&flags, 1), flags(flags), vel(vel), force(force)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(
|
|
int i, int j, int k, const FlagGrid &flags, MACGrid &vel, const Grid<Vec3> &force) const
|
|
{
|
|
bool curFluid = flags.isFluid(i, j, k);
|
|
bool curEmpty = flags.isEmpty(i, j, k);
|
|
if (!curFluid && !curEmpty)
|
|
return;
|
|
|
|
Real minVal, maxVal, sum;
|
|
if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k))) {
|
|
Real forceMACX = 0.5 * (force(i - 1, j, k).x + force(i, j, k).x);
|
|
minVal = min(vel(i, j, k).x, forceMACX);
|
|
maxVal = max(vel(i, j, k).x, forceMACX);
|
|
sum = vel(i, j, k).x + forceMACX;
|
|
vel(i, j, k).x = (forceMACX > 0) ? min(sum, maxVal) : max(sum, minVal);
|
|
}
|
|
if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k))) {
|
|
Real forceMACY = 0.5 * (force(i, j - 1, k).y + force(i, j, k).y);
|
|
minVal = min(vel(i, j, k).y, forceMACY);
|
|
maxVal = max(vel(i, j, k).y, forceMACY);
|
|
sum = vel(i, j, k).y + forceMACY;
|
|
vel(i, j, k).y = (forceMACY > 0) ? min(sum, maxVal) : max(sum, minVal);
|
|
}
|
|
if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1)))) {
|
|
Real forceMACZ = 0.5 * (force(i, j, k - 1).z + force(i, j, k).z);
|
|
minVal = min(vel(i, j, k).z, forceMACZ);
|
|
maxVal = max(vel(i, j, k).z, forceMACZ);
|
|
sum = vel(i, j, k).z + forceMACZ;
|
|
vel(i, j, k).z = (forceMACZ > 0) ? min(sum, maxVal) : max(sum, minVal);
|
|
}
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline MACGrid &getArg1()
|
|
{
|
|
return vel;
|
|
}
|
|
typedef MACGrid type1;
|
|
inline const Grid<Vec3> &getArg2()
|
|
{
|
|
return force;
|
|
}
|
|
typedef Grid<Vec3> type2;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnAddForceIfLower ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, force);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, flags, vel, force);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
MACGrid &vel;
|
|
const Grid<Vec3> &force;
|
|
};
|
|
|
|
// Initial velocity for smoke
|
|
void setInitialVelocity(const FlagGrid &flags, MACGrid &vel, const Grid<Vec3> &invel)
|
|
{
|
|
KnAddForceIfLower(flags, vel, invel);
|
|
}
|
|
static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "setInitialVelocity", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
|
|
const Grid<Vec3> &invel = *_args.getPtr<Grid<Vec3>>("invel", 2, &_lock);
|
|
_retval = getPyNone();
|
|
setInitialVelocity(flags, vel, invel);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "setInitialVelocity", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("setInitialVelocity", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_setInitialVelocity("", "setInitialVelocity", _W_7);
|
|
extern "C" {
|
|
void PbRegister_setInitialVelocity()
|
|
{
|
|
KEEP_UNUSED(_RP_setInitialVelocity);
|
|
}
|
|
}
|
|
|
|
//! Kernel: gradient norm operator
|
|
struct KnConfForce : public KernelBase {
|
|
KnConfForce(Grid<Vec3> &force,
|
|
const Grid<Real> &grid,
|
|
const Grid<Vec3> &curl,
|
|
Real str,
|
|
const Grid<Real> *strGrid)
|
|
: KernelBase(&force, 1), force(force), grid(grid), curl(curl), str(str), strGrid(strGrid)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
Grid<Vec3> &force,
|
|
const Grid<Real> &grid,
|
|
const Grid<Vec3> &curl,
|
|
Real str,
|
|
const Grid<Real> *strGrid) const
|
|
{
|
|
Vec3 grad = 0.5 * Vec3(grid(i + 1, j, k) - grid(i - 1, j, k),
|
|
grid(i, j + 1, k) - grid(i, j - 1, k),
|
|
0.);
|
|
if (grid.is3D())
|
|
grad[2] = 0.5 * (grid(i, j, k + 1) - grid(i, j, k - 1));
|
|
normalize(grad);
|
|
if (strGrid)
|
|
str += (*strGrid)(i, j, k);
|
|
force(i, j, k) = str * cross(grad, curl(i, j, k));
|
|
}
|
|
inline Grid<Vec3> &getArg0()
|
|
{
|
|
return force;
|
|
}
|
|
typedef Grid<Vec3> type0;
|
|
inline const Grid<Real> &getArg1()
|
|
{
|
|
return grid;
|
|
}
|
|
typedef Grid<Real> type1;
|
|
inline const Grid<Vec3> &getArg2()
|
|
{
|
|
return curl;
|
|
}
|
|
typedef Grid<Vec3> type2;
|
|
inline Real &getArg3()
|
|
{
|
|
return str;
|
|
}
|
|
typedef Real type3;
|
|
inline const Grid<Real> *getArg4()
|
|
{
|
|
return strGrid;
|
|
}
|
|
typedef Grid<Real> type4;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnConfForce ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 1; j < _maxY; j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, force, grid, curl, str, strGrid);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 1; i < _maxX; i++)
|
|
op(i, j, k, force, grid, curl, str, strGrid);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
|
}
|
|
Grid<Vec3> &force;
|
|
const Grid<Real> &grid;
|
|
const Grid<Vec3> &curl;
|
|
Real str;
|
|
const Grid<Real> *strGrid;
|
|
};
|
|
|
|
void vorticityConfinement(MACGrid &vel,
|
|
const FlagGrid &flags,
|
|
Real strength = 0,
|
|
const Grid<Real> *strengthCell = nullptr)
|
|
{
|
|
Grid<Vec3> velCenter(flags.getParent()), curl(flags.getParent()), force(flags.getParent());
|
|
Grid<Real> norm(flags.getParent());
|
|
|
|
GetCentered(velCenter, vel);
|
|
CurlOp(velCenter, curl);
|
|
GridNorm(norm, curl);
|
|
KnConfForce(force, norm, curl, strength, strengthCell);
|
|
KnApplyForceField(flags, vel, force, nullptr, true, false);
|
|
}
|
|
static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "vorticityConfinement", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 0, &_lock);
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 1, &_lock);
|
|
Real strength = _args.getOpt<Real>("strength", 2, 0, &_lock);
|
|
const Grid<Real> *strengthCell = _args.getPtrOpt<Grid<Real>>(
|
|
"strengthCell", 3, nullptr, &_lock);
|
|
_retval = getPyNone();
|
|
vorticityConfinement(vel, flags, strength, strengthCell);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "vorticityConfinement", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("vorticityConfinement", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_vorticityConfinement("", "vorticityConfinement", _W_8);
|
|
extern "C" {
|
|
void PbRegister_vorticityConfinement()
|
|
{
|
|
KEEP_UNUSED(_RP_vorticityConfinement);
|
|
}
|
|
}
|
|
|
|
void addForceField(const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
const Grid<Vec3> &force,
|
|
const Grid<Real> *region = nullptr,
|
|
bool isMAC = false)
|
|
{
|
|
KnApplyForceField(flags, vel, force, region, true, isMAC);
|
|
}
|
|
static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "addForceField", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
|
|
const Grid<Vec3> &force = *_args.getPtr<Grid<Vec3>>("force", 2, &_lock);
|
|
const Grid<Real> *region = _args.getPtrOpt<Grid<Real>>("region", 3, nullptr, &_lock);
|
|
bool isMAC = _args.getOpt<bool>("isMAC", 4, false, &_lock);
|
|
_retval = getPyNone();
|
|
addForceField(flags, vel, force, region, isMAC);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "addForceField", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("addForceField", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_addForceField("", "addForceField", _W_9);
|
|
extern "C" {
|
|
void PbRegister_addForceField()
|
|
{
|
|
KEEP_UNUSED(_RP_addForceField);
|
|
}
|
|
}
|
|
|
|
void setForceField(const FlagGrid &flags,
|
|
MACGrid &vel,
|
|
const Grid<Vec3> &force,
|
|
const Grid<Real> *region = nullptr,
|
|
bool isMAC = false)
|
|
{
|
|
KnApplyForceField(flags, vel, force, region, false, isMAC);
|
|
}
|
|
static PyObject *_W_10(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "setForceField", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
|
|
const Grid<Vec3> &force = *_args.getPtr<Grid<Vec3>>("force", 2, &_lock);
|
|
const Grid<Real> *region = _args.getPtrOpt<Grid<Real>>("region", 3, nullptr, &_lock);
|
|
bool isMAC = _args.getOpt<bool>("isMAC", 4, false, &_lock);
|
|
_retval = getPyNone();
|
|
setForceField(flags, vel, force, region, isMAC);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "setForceField", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("setForceField", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_setForceField("", "setForceField", _W_10);
|
|
extern "C" {
|
|
void PbRegister_setForceField()
|
|
{
|
|
KEEP_UNUSED(_RP_setForceField);
|
|
}
|
|
}
|
|
|
|
struct KnDissolveSmoke : public KernelBase {
|
|
KnDissolveSmoke(const FlagGrid &flags,
|
|
Grid<Real> &density,
|
|
Grid<Real> *heat,
|
|
Grid<Real> *red,
|
|
Grid<Real> *green,
|
|
Grid<Real> *blue,
|
|
int speed,
|
|
bool logFalloff,
|
|
float dydx,
|
|
float fac)
|
|
: KernelBase(&flags, 0),
|
|
flags(flags),
|
|
density(density),
|
|
heat(heat),
|
|
red(red),
|
|
green(green),
|
|
blue(blue),
|
|
speed(speed),
|
|
logFalloff(logFalloff),
|
|
dydx(dydx),
|
|
fac(fac)
|
|
{
|
|
runMessage();
|
|
run();
|
|
}
|
|
inline void op(int i,
|
|
int j,
|
|
int k,
|
|
const FlagGrid &flags,
|
|
Grid<Real> &density,
|
|
Grid<Real> *heat,
|
|
Grid<Real> *red,
|
|
Grid<Real> *green,
|
|
Grid<Real> *blue,
|
|
int speed,
|
|
bool logFalloff,
|
|
float dydx,
|
|
float fac) const
|
|
{
|
|
|
|
bool curFluid = flags.isFluid(i, j, k);
|
|
if (!curFluid)
|
|
return;
|
|
|
|
if (logFalloff) {
|
|
density(i, j, k) *= fac;
|
|
if (heat) {
|
|
(*heat)(i, j, k) *= fac;
|
|
}
|
|
if (red) {
|
|
(*red)(i, j, k) *= fac;
|
|
(*green)(i, j, k) *= fac;
|
|
(*blue)(i, j, k) *= fac;
|
|
}
|
|
}
|
|
else { // linear falloff
|
|
float d = density(i, j, k);
|
|
density(i, j, k) -= dydx;
|
|
if (density(i, j, k) < 0.0f)
|
|
density(i, j, k) = 0.0f;
|
|
if (heat) {
|
|
if (fabs((*heat)(i, j, k)) < dydx)
|
|
(*heat)(i, j, k) = 0.0f;
|
|
else if ((*heat)(i, j, k) > 0.0f)
|
|
(*heat)(i, j, k) -= dydx;
|
|
else if ((*heat)(i, j, k) < 0.0f)
|
|
(*heat)(i, j, k) += dydx;
|
|
}
|
|
if (red && notZero(d)) {
|
|
(*red)(i, j, k) *= (density(i, j, k) / d);
|
|
(*green)(i, j, k) *= (density(i, j, k) / d);
|
|
(*blue)(i, j, k) *= (density(i, j, k) / d);
|
|
}
|
|
}
|
|
}
|
|
inline const FlagGrid &getArg0()
|
|
{
|
|
return flags;
|
|
}
|
|
typedef FlagGrid type0;
|
|
inline Grid<Real> &getArg1()
|
|
{
|
|
return density;
|
|
}
|
|
typedef Grid<Real> type1;
|
|
inline Grid<Real> *getArg2()
|
|
{
|
|
return heat;
|
|
}
|
|
typedef Grid<Real> type2;
|
|
inline Grid<Real> *getArg3()
|
|
{
|
|
return red;
|
|
}
|
|
typedef Grid<Real> type3;
|
|
inline Grid<Real> *getArg4()
|
|
{
|
|
return green;
|
|
}
|
|
typedef Grid<Real> type4;
|
|
inline Grid<Real> *getArg5()
|
|
{
|
|
return blue;
|
|
}
|
|
typedef Grid<Real> type5;
|
|
inline int &getArg6()
|
|
{
|
|
return speed;
|
|
}
|
|
typedef int type6;
|
|
inline bool &getArg7()
|
|
{
|
|
return logFalloff;
|
|
}
|
|
typedef bool type7;
|
|
inline float &getArg8()
|
|
{
|
|
return dydx;
|
|
}
|
|
typedef float type8;
|
|
inline float &getArg9()
|
|
{
|
|
return fac;
|
|
}
|
|
typedef float type9;
|
|
void runMessage()
|
|
{
|
|
debMsg("Executing kernel KnDissolveSmoke ", 3);
|
|
debMsg("Kernel range"
|
|
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
|
4);
|
|
};
|
|
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
|
{
|
|
const int _maxX = maxX;
|
|
const int _maxY = maxY;
|
|
if (maxZ > 1) {
|
|
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
|
for (int j = 0; j < _maxY; j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, flags, density, heat, red, green, blue, speed, logFalloff, dydx, fac);
|
|
}
|
|
else {
|
|
const int k = 0;
|
|
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
|
for (int i = 0; i < _maxX; i++)
|
|
op(i, j, k, flags, density, heat, red, green, blue, speed, logFalloff, dydx, fac);
|
|
}
|
|
}
|
|
void run()
|
|
{
|
|
if (maxZ > 1)
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
|
else
|
|
tbb::parallel_for(tbb::blocked_range<IndexInt>(0, maxY), *this);
|
|
}
|
|
const FlagGrid &flags;
|
|
Grid<Real> &density;
|
|
Grid<Real> *heat;
|
|
Grid<Real> *red;
|
|
Grid<Real> *green;
|
|
Grid<Real> *blue;
|
|
int speed;
|
|
bool logFalloff;
|
|
float dydx;
|
|
float fac;
|
|
};
|
|
|
|
void dissolveSmoke(const FlagGrid &flags,
|
|
Grid<Real> &density,
|
|
Grid<Real> *heat = nullptr,
|
|
Grid<Real> *red = nullptr,
|
|
Grid<Real> *green = nullptr,
|
|
Grid<Real> *blue = nullptr,
|
|
int speed = 5,
|
|
bool logFalloff = true)
|
|
{
|
|
float dydx = 1.0f / (float)speed; // max density/speed = dydx
|
|
float fac = 1.0f - dydx;
|
|
KnDissolveSmoke(flags, density, heat, red, green, blue, speed, logFalloff, dydx, fac);
|
|
}
|
|
static PyObject *_W_11(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "dissolveSmoke", !noTiming);
|
|
PyObject *_retval = nullptr;
|
|
{
|
|
ArgLocker _lock;
|
|
const FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
|
|
Grid<Real> &density = *_args.getPtr<Grid<Real>>("density", 1, &_lock);
|
|
Grid<Real> *heat = _args.getPtrOpt<Grid<Real>>("heat", 2, nullptr, &_lock);
|
|
Grid<Real> *red = _args.getPtrOpt<Grid<Real>>("red", 3, nullptr, &_lock);
|
|
Grid<Real> *green = _args.getPtrOpt<Grid<Real>>("green", 4, nullptr, &_lock);
|
|
Grid<Real> *blue = _args.getPtrOpt<Grid<Real>>("blue", 5, nullptr, &_lock);
|
|
int speed = _args.getOpt<int>("speed", 6, 5, &_lock);
|
|
bool logFalloff = _args.getOpt<bool>("logFalloff", 7, true, &_lock);
|
|
_retval = getPyNone();
|
|
dissolveSmoke(flags, density, heat, red, green, blue, speed, logFalloff);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "dissolveSmoke", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("dissolveSmoke", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_dissolveSmoke("", "dissolveSmoke", _W_11);
|
|
extern "C" {
|
|
void PbRegister_dissolveSmoke()
|
|
{
|
|
KEEP_UNUSED(_RP_dissolveSmoke);
|
|
}
|
|
}
|
|
|
|
} // namespace Manta
|