// DO NOT EDIT ! // This file is generated using the MantaFlow preprocessor (prep generate). /****************************************************************************** * * MantaFlow fluid solver framework * Copyright 2020 Sebastian Barschkis, Nils Thuerey * * This program is free software, distributed under the terms of the * Apache License, Version 2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Accurate Viscous Free Surfaces for Buckling, Coiling, and Rotating Liquids * Batty et al., SCA 2008 * ******************************************************************************/ #include "conjugategrad.h" #include "general.h" #include "grid.h" #include "vectorbase.h" #include #if OPENMP == 1 || TBB == 1 # define ENABLE_PARALLEL 0 #endif #if ENABLE_PARALLEL == 1 # include # include static const int manta_num_threads = std::thread::hardware_concurrency(); # define parallel_block \ { \ std::vector threads; \ { # define do_parallel threads.push_back( std::thread([&]() { # define do_end \ } ) ); # define block_end \ } \ for (auto &thread : threads) { \ thread.join(); \ } \ } #endif #define FOR_INT_IJK(num) \ for (int k_off = 0; k_off < num; ++k_off) \ for (int j_off = 0; j_off < num; ++j_off) \ for (int i_off = 0; i_off < num; ++i_off) using namespace std; namespace Manta { //! Assumes phi0<0 and phi1>=0, phi2>=0, and phi3>=0 or vice versa. //! In particular, phi0 must not equal any of phi1, phi2 or phi3. static Real sortedTetFraction(Real phi0, Real phi1, Real phi2, Real phi3) { return phi0 * phi0 * phi0 / ((phi0 - phi1) * (phi0 - phi2) * (phi0 - phi3)); } //! Assumes phi0<0, phi1<0, and phi2>=0, and phi3>=0 or vice versa. //! In particular, phi0 and phi1 must not equal any of phi2 and phi3. static Real sortedPrismFraction(Real phi0, Real phi1, Real phi2, Real phi3) { Real a = phi0 / (phi0 - phi2); Real b = phi0 / (phi0 - phi3); Real c = phi1 / (phi1 - phi3); Real d = phi1 / (phi1 - phi2); return a * b * (1 - d) + b * (1 - c) * d + c * d; } Real volumeFraction(Real phi0, Real phi1, Real phi2, Real phi3) { sort(phi0, phi1, phi2, phi3); if (phi3 <= 0) return 1; else if (phi2 <= 0) return 1 - sortedTetFraction(phi3, phi2, phi1, phi0); else if (phi1 <= 0) return sortedPrismFraction(phi0, phi1, phi2, phi3); else if (phi0 <= 0) return sortedTetFraction(phi0, phi1, phi2, phi3); else return 0; } //! The average of the two possible decompositions of the cube into five tetrahedra. Real volumeFraction(Real phi000, Real phi100, Real phi010, Real phi110, Real phi001, Real phi101, Real phi011, Real phi111) { return (volumeFraction(phi000, phi001, phi101, phi011) + volumeFraction(phi000, phi101, phi100, phi110) + volumeFraction(phi000, phi010, phi011, phi110) + volumeFraction(phi101, phi011, phi111, phi110) + 2 * volumeFraction(phi000, phi011, phi101, phi110) + volumeFraction(phi100, phi101, phi001, phi111) + volumeFraction(phi100, phi001, phi000, phi010) + volumeFraction(phi100, phi110, phi111, phi010) + volumeFraction(phi001, phi111, phi011, phi010) + 2 * volumeFraction(phi100, phi111, phi001, phi010)) / 12; } //! Kernel loop over grid with 2x base resolution! struct KnEstimateVolumeFraction : public KernelBase { KnEstimateVolumeFraction(Grid &volumes, const Grid &phi, const Vec3 &startCentre, const Real dx) : KernelBase(&volumes, 0), volumes(volumes), phi(phi), startCentre(startCentre), dx(dx) { runMessage(); run(); } inline void op(int i, int j, int k, Grid &volumes, const Grid &phi, const Vec3 &startCentre, const Real dx) const { const Vec3 centre = startCentre + Vec3(i, j, k) * 0.5; const Real offset = 0.5 * dx; const int order = 1; // is sufficient Real phi000 = phi.getInterpolatedHi(centre + Vec3(-offset, -offset, -offset), order); Real phi001 = phi.getInterpolatedHi(centre + Vec3(-offset, -offset, +offset), order); Real phi010 = phi.getInterpolatedHi(centre + Vec3(-offset, +offset, -offset), order); Real phi011 = phi.getInterpolatedHi(centre + Vec3(-offset, +offset, +offset), order); Real phi100 = phi.getInterpolatedHi(centre + Vec3(+offset, -offset, -offset), order); Real phi101 = phi.getInterpolatedHi(centre + Vec3(+offset, -offset, +offset), order); Real phi110 = phi.getInterpolatedHi(centre + Vec3(+offset, +offset, -offset), order); Real phi111 = phi.getInterpolatedHi(centre + Vec3(+offset, +offset, +offset), order); volumes(i, j, k) = volumeFraction( phi000, phi100, phi010, phi110, phi001, phi101, phi011, phi111); } inline Grid &getArg0() { return volumes; } typedef Grid type0; inline const Grid &getArg1() { return phi; } typedef Grid type1; inline const Vec3 &getArg2() { return startCentre; } typedef Vec3 type2; inline const Real &getArg3() { return dx; } typedef Real type3; void runMessage() { debMsg("Executing kernel KnEstimateVolumeFraction ", 3); debMsg("Kernel range" << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", 4); }; void operator()(const tbb::blocked_range &__r) const { const int _maxX = maxX; const int _maxY = maxY; if (maxZ > 1) { for (int k = __r.begin(); k != (int)__r.end(); k++) for (int j = 0; j < _maxY; j++) for (int i = 0; i < _maxX; i++) op(i, j, k, volumes, phi, startCentre, dx); } 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, volumes, phi, startCentre, dx); } } void run() { if (maxZ > 1) tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } Grid &volumes; const Grid φ const Vec3 &startCentre; const Real dx; }; struct KnUpdateVolumeGrid : public KernelBase { KnUpdateVolumeGrid(Grid &cVolLiquid, Grid &uVolLiquid, Grid &vVolLiquid, Grid &wVolLiquid, Grid &exVolLiquid, Grid &eyVolLiquid, Grid &ezVolLiquid, const Grid &src) : KernelBase(&cVolLiquid, 0), cVolLiquid(cVolLiquid), uVolLiquid(uVolLiquid), vVolLiquid(vVolLiquid), wVolLiquid(wVolLiquid), exVolLiquid(exVolLiquid), eyVolLiquid(eyVolLiquid), ezVolLiquid(ezVolLiquid), src(src) { runMessage(); run(); } inline void op(int i, int j, int k, Grid &cVolLiquid, Grid &uVolLiquid, Grid &vVolLiquid, Grid &wVolLiquid, Grid &exVolLiquid, Grid &eyVolLiquid, Grid &ezVolLiquid, const Grid &src) const { // Work out c cVolLiquid(i, j, k) = 0; FOR_INT_IJK(2) { cVolLiquid(i, j, k) += src(2 * i + i_off, 2 * j + j_off, 2 * k + k_off); } cVolLiquid(i, j, k) /= 8; // Work out u if (i >= 1) { uVolLiquid(i, j, k) = 0; int base_i = 2 * i - 1; int base_j = 2 * j; int base_k = 2 * k; FOR_INT_IJK(2) { uVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); } uVolLiquid(i, j, k) /= 8; } // v if (j >= 1) { vVolLiquid(i, j, k) = 0; int base_i = 2 * i; int base_j = 2 * j - 1; int base_k = 2 * k; FOR_INT_IJK(2) { vVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); } vVolLiquid(i, j, k) /= 8; } // w if (k >= 1) { wVolLiquid(i, j, k) = 0; int base_i = 2 * i; int base_j = 2 * j; int base_k = 2 * k - 1; FOR_INT_IJK(2) { wVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); } wVolLiquid(i, j, k) /= 8; } // e-x if (j >= 1 && k >= 1) { exVolLiquid(i, j, k) = 0; int base_i = 2 * i; int base_j = 2 * j - 1; int base_k = 2 * k - 1; FOR_INT_IJK(2) { exVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); } exVolLiquid(i, j, k) /= 8; } // e-y if (i >= 1 && k >= 1) { eyVolLiquid(i, j, k) = 0; int base_i = 2 * i - 1; int base_j = 2 * j; int base_k = 2 * k - 1; FOR_INT_IJK(2) { eyVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); } eyVolLiquid(i, j, k) /= 8; } // e-z if (i >= 1 && j >= 1) { ezVolLiquid(i, j, k) = 0; int base_i = 2 * i - 1; int base_j = 2 * j - 1; int base_k = 2 * k; FOR_INT_IJK(2) { ezVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); } ezVolLiquid(i, j, k) /= 8; } } inline Grid &getArg0() { return cVolLiquid; } typedef Grid type0; inline Grid &getArg1() { return uVolLiquid; } typedef Grid type1; inline Grid &getArg2() { return vVolLiquid; } typedef Grid type2; inline Grid &getArg3() { return wVolLiquid; } typedef Grid type3; inline Grid &getArg4() { return exVolLiquid; } typedef Grid type4; inline Grid &getArg5() { return eyVolLiquid; } typedef Grid type5; inline Grid &getArg6() { return ezVolLiquid; } typedef Grid type6; inline const Grid &getArg7() { return src; } typedef Grid type7; void runMessage() { debMsg("Executing kernel KnUpdateVolumeGrid ", 3); debMsg("Kernel range" << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", 4); }; void operator()(const tbb::blocked_range &__r) const { const int _maxX = maxX; const int _maxY = maxY; if (maxZ > 1) { for (int k = __r.begin(); k != (int)__r.end(); k++) for (int j = 0; j < _maxY; j++) for (int i = 0; i < _maxX; i++) op(i, j, k, cVolLiquid, uVolLiquid, vVolLiquid, wVolLiquid, exVolLiquid, eyVolLiquid, ezVolLiquid, src); } 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, cVolLiquid, uVolLiquid, vVolLiquid, wVolLiquid, exVolLiquid, eyVolLiquid, ezVolLiquid, src); } } void run() { if (maxZ > 1) tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } Grid &cVolLiquid; Grid &uVolLiquid; Grid &vVolLiquid; Grid &wVolLiquid; Grid &exVolLiquid; Grid &eyVolLiquid; Grid &ezVolLiquid; const Grid &src; }; void computeWeights(const Grid &phi, Grid &doubleSized, Grid &cVolLiquid, Grid &uVolLiquid, Grid &vVolLiquid, Grid &wVolLiquid, Grid &exVolLiquid, Grid &eyVolLiquid, Grid &ezVolLiquid, Real dx) { KnEstimateVolumeFraction(doubleSized, phi, Vec3(0.25 * dx, 0.25 * dx, 0.25 * dx), 0.5 * dx); KnUpdateVolumeGrid(cVolLiquid, uVolLiquid, vVolLiquid, wVolLiquid, exVolLiquid, eyVolLiquid, ezVolLiquid, doubleSized); } struct KnUpdateFaceStates : public KernelBase { KnUpdateFaceStates(const FlagGrid &flags, Grid &uState, Grid &vState, Grid &wState) : KernelBase(&flags, 0), flags(flags), uState(uState), vState(vState), wState(wState) { runMessage(); run(); } inline void op(int i, int j, int k, const FlagGrid &flags, Grid &uState, Grid &vState, Grid &wState) const { bool curObs = flags.isObstacle(i, j, k); uState(i, j, k) = (i > 0 && !flags.isObstacle(i - 1, j, k) && !curObs) ? FlagGrid::TypeFluid : FlagGrid::TypeObstacle; vState(i, j, k) = (j > 0 && !flags.isObstacle(i, j - 1, k) && !curObs) ? FlagGrid::TypeFluid : FlagGrid::TypeObstacle; wState(i, j, k) = (k > 0 && !flags.isObstacle(i, j, k - 1) && !curObs) ? FlagGrid::TypeFluid : FlagGrid::TypeObstacle; } inline const FlagGrid &getArg0() { return flags; } typedef FlagGrid type0; inline Grid &getArg1() { return uState; } typedef Grid type1; inline Grid &getArg2() { return vState; } typedef Grid type2; inline Grid &getArg3() { return wState; } typedef Grid type3; void runMessage() { debMsg("Executing kernel KnUpdateFaceStates ", 3); debMsg("Kernel range" << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", 4); }; void operator()(const tbb::blocked_range &__r) const { const int _maxX = maxX; const int _maxY = maxY; if (maxZ > 1) { for (int k = __r.begin(); k != (int)__r.end(); k++) for (int j = 0; j < _maxY; j++) for (int i = 0; i < _maxX; i++) op(i, j, k, flags, uState, vState, wState); } 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, uState, vState, wState); } } void run() { if (maxZ > 1) tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } const FlagGrid &flags; Grid &uState; Grid &vState; Grid &wState; }; struct KnApplyVelocities : public KernelBase { KnApplyVelocities(MACGrid &dst, const Grid &uState, const Grid &vState, const Grid &wState, Grid &srcU, Grid &srcV, Grid &srcW) : KernelBase(&dst, 0), dst(dst), uState(uState), vState(vState), wState(wState), srcU(srcU), srcV(srcV), srcW(srcW) { runMessage(); run(); } inline void op(int i, int j, int k, MACGrid &dst, const Grid &uState, const Grid &vState, const Grid &wState, Grid &srcU, Grid &srcV, Grid &srcW) const { dst(i, j, k).x = (uState(i, j, k) == FlagGrid::TypeFluid) ? srcU(i, j, k) : 0; dst(i, j, k).y = (vState(i, j, k) == FlagGrid::TypeFluid) ? srcV(i, j, k) : 0; if (dst.is3D()) dst(i, j, k).z = (wState(i, j, k) == FlagGrid::TypeFluid) ? srcW(i, j, k) : 0; } inline MACGrid &getArg0() { return dst; } typedef MACGrid type0; inline const Grid &getArg1() { return uState; } typedef Grid type1; inline const Grid &getArg2() { return vState; } typedef Grid type2; inline const Grid &getArg3() { return wState; } typedef Grid type3; inline Grid &getArg4() { return srcU; } typedef Grid type4; inline Grid &getArg5() { return srcV; } typedef Grid type5; inline Grid &getArg6() { return srcW; } typedef Grid type6; void runMessage() { debMsg("Executing kernel KnApplyVelocities ", 3); debMsg("Kernel range" << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", 4); }; void operator()(const tbb::blocked_range &__r) const { const int _maxX = maxX; const int _maxY = maxY; if (maxZ > 1) { for (int k = __r.begin(); k != (int)__r.end(); k++) for (int j = 0; j < _maxY; j++) for (int i = 0; i < _maxX; i++) op(i, j, k, dst, uState, vState, wState, srcU, srcV, srcW); } 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, dst, uState, vState, wState, srcU, srcV, srcW); } } void run() { if (maxZ > 1) tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); else tbb::parallel_for(tbb::blocked_range(0, maxY), *this); } MACGrid &dst; const Grid &uState; const Grid &vState; const Grid &wState; Grid &srcU; Grid &srcV; Grid &srcW; }; void solveViscosity(const FlagGrid &flags, MACGrid &vel, Grid &cVolLiquid, Grid &uVolLiquid, Grid &vVolLiquid, Grid &wVolLiquid, Grid &exVolLiquid, Grid &eyVolLiquid, Grid &ezVolLiquid, Grid &viscosity, const Real dt, const Real dx, const Real cgAccuracy, const Real cgMaxIterFac) { const Real factor = dt * square(1.0 / dx); const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4); GridCg *uGcg; GridCg *vGcg; GridCg *wGcg; // Tmp grids for CG solve in U, V, W dimensions FluidSolver *parent = flags.getParent(); Grid uResidual(parent); Grid vResidual(parent); Grid wResidual(parent); Grid uSearch(parent); Grid vSearch(parent); Grid wSearch(parent); Grid uTmp(parent); Grid vTmp(parent); Grid wTmp(parent); Grid uRhs(parent); Grid vRhs(parent); Grid wRhs(parent); // A matrix U grids Grid uA0(parent); // diagonal elements in A matrix Grid uAplusi(parent); // neighbor at i+1 Grid uAplusj(parent); // neighbor at j+1 Grid uAplusk(parent); // neighbor at k+1 Grid uAminusi(parent); // neighbor at i-1 Grid uAminusj(parent); // neighbor at j-1 Grid uAminusk(parent); // neighbor at k-1 Grid uAhelper1(parent); // additional helper grids for off diagonal elements Grid uAhelper2(parent); Grid uAhelper3(parent); Grid uAhelper4(parent); Grid uAhelper5(parent); Grid uAhelper6(parent); Grid uAhelper7(parent); Grid uAhelper8(parent); // A matrix V grids Grid vA0(parent); Grid vAplusi(parent); Grid vAplusj(parent); Grid vAplusk(parent); Grid vAminusi(parent); Grid vAminusj(parent); Grid vAminusk(parent); Grid vAhelper1(parent); Grid vAhelper2(parent); Grid vAhelper3(parent); Grid vAhelper4(parent); Grid vAhelper5(parent); Grid vAhelper6(parent); Grid vAhelper7(parent); Grid vAhelper8(parent); // A matrix W grids Grid wA0(parent); Grid wAplusi(parent); Grid wAplusj(parent); Grid wAplusk(parent); Grid wAminusi(parent); Grid wAminusj(parent); Grid wAminusk(parent); Grid wAhelper1(parent); Grid wAhelper2(parent); Grid wAhelper3(parent); Grid wAhelper4(parent); Grid wAhelper5(parent); Grid wAhelper6(parent); Grid wAhelper7(parent); Grid wAhelper8(parent); // Solution grids for CG solvers Grid uSolution(parent); Grid vSolution(parent); Grid wSolution(parent); // Save state of voxel face (fluid or obstacle) Grid uState(parent); Grid vState(parent); Grid wState(parent); // Save state of voxel face (fluid or obstacle) KnUpdateFaceStates(flags, uState, vState, wState); // Shorter names for flags, we will use them often int isFluid = FlagGrid::TypeFluid; int isObstacle = FlagGrid::TypeObstacle; // Main viscosity loop: construct A matrices and rhs's in all dimensions FOR_IJK_BND(flags, 1) { // U-terms: 2u_xx+ v_xy +uyy + u_zz + w_xz if (uState(i, j, k) == isFluid) { uRhs(i, j, k) = uVolLiquid(i, j, k) * vel(i, j, k).x; uA0(i, j, k) = uVolLiquid(i, j, k); Real viscRight = viscosity(i, j, k); Real viscLeft = viscosity(i - 1, j, k); Real volRight = cVolLiquid(i, j, k); Real volLeft = cVolLiquid(i - 1, j, k); Real viscTop = 0.25 * (viscosity(i - 1, j + 1, k) + viscosity(i - 1, j, k) + viscosity(i, j + 1, k) + viscosity(i, j, k)); Real viscBottom = 0.25 * (viscosity(i - 1, j, k) + viscosity(i - 1, j - 1, k) + viscosity(i, j, k) + viscosity(i, j - 1, k)); Real volTop = ezVolLiquid(i, j + 1, k); Real volBottom = ezVolLiquid(i, j, k); Real viscFront = 0.25 * (viscosity(i - 1, j, k + 1) + viscosity(i - 1, j, k) + viscosity(i, j, k + 1) + viscosity(i, j, k)); Real viscBack = 0.25 * (viscosity(i - 1, j, k) + viscosity(i - 1, j, k - 1) + viscosity(i, j, k) + viscosity(i, j, k - 1)); Real volFront = eyVolLiquid(i, j, k + 1); Real volBack = eyVolLiquid(i, j, k); Real factorRight = 2 * factor * viscRight * volRight; Real factorLeft = 2 * factor * viscLeft * volLeft; Real factorTop = factor * viscTop * volTop; Real factorBottom = factor * viscBottom * volBottom; Real factorFront = factor * viscFront * volFront; Real factorBack = factor * viscBack * volBack; // u_x_right uA0(i, j, k) += factorRight; if (uState(i + 1, j, k) == isFluid) { uAplusi(i, j, k) += -factorRight; } else if (uState(i + 1, j, k) == isObstacle) { uRhs(i, j, k) -= -vel(i + 1, j, k).x * factorRight; } // u_x_left uA0(i, j, k) += factorLeft; if (uState(i - 1, j, k) == isFluid) { uAminusi(i, j, k) += -factorLeft; } else if (uState(i - 1, j, k) == isObstacle) { uRhs(i, j, k) -= -vel(i - 1, j, k).x * factorLeft; } // u_y_top uA0(i, j, k) += factorTop; if (uState(i, j + 1, k) == isFluid) { uAplusj(i, j, k) += -factorTop; } else if (uState(i, j + 1, k) == isObstacle) { uRhs(i, j, k) -= -vel(i, j + 1, k).x * factorTop; } // u_y_bottom uA0(i, j, k) += factorBottom; if (uState(i, j - 1, k) == isFluid) { uAminusj(i, j, k) += -factorBottom; } else if (uState(i, j - 1, k) == isObstacle) { uRhs(i, j, k) -= -vel(i, j - 1, k).x * factorBottom; } // u_z_front uA0(i, j, k) += factorFront; if (uState(i, j, k + 1) == isFluid) { uAplusk(i, j, k) += -factorFront; } else if (uState(i, j, k + 1) == isObstacle) { uRhs(i, j, k) -= -vel(i, j, k + 1).x * factorFront; } // u_z_back uA0(i, j, k) += factorBack; if (uState(i, j, k - 1) == isFluid) { uAminusk(i, j, k) += -factorBack; } else if (uState(i, j, k - 1) == isObstacle) { uRhs(i, j, k) -= -vel(i, j, k - 1).x * factorBack; } // v_x_top if (vState(i, j + 1, k) == isFluid) { uAhelper1(i, j, k) += -factorTop; } else if (vState(i, j + 1, k) == isObstacle) { uRhs(i, j, k) -= -vel(i, j + 1, k).y * factorTop; } if (vState(i - 1, j + 1, k) == isFluid) { uAhelper2(i, j, k) += factorTop; } else if (vState(i - 1, j + 1, k) == isObstacle) { uRhs(i, j, k) -= vel(i - 1, j + 1, k).y * factorTop; } // v_x_bottom if (vState(i, j, k) == isFluid) { uAhelper3(i, j, k) += factorBottom; } else if (vState(i, j, k) == isObstacle) { uRhs(i, j, k) -= vel(i, j, k).y * factorBottom; } if (vState(i - 1, j, k) == isFluid) { uAhelper4(i, j, k) += -factorBottom; } else if (vState(i - 1, j, k) == isObstacle) { uRhs(i, j, k) -= -vel(i - 1, j, k).y * factorBottom; } // w_x_front if (wState(i, j, k + 1) == isFluid) { uAhelper5(i, j, k) += -factorFront; } else if (wState(i, j, k + 1) == isObstacle) { uRhs(i, j, k) -= -vel(i, j, k + 1).z * factorFront; } if (wState(i - 1, j, k + 1) == isFluid) { uAhelper6(i, j, k) += factorFront; } else if (wState(i - 1, j, k + 1) == isObstacle) { uRhs(i, j, k) -= vel(i - 1, j, k + 1).z * factorFront; } // w_x_back if (wState(i, j, k) == isFluid) { uAhelper7(i, j, k) += factorBack; } else if (wState(i, j, k) == isObstacle) { uRhs(i, j, k) -= vel(i, j, k).z * factorBack; } if (wState(i - 1, j, k) == isFluid) { uAhelper8(i, j, k) += -factorBack; } else if (wState(i - 1, j, k) == isObstacle) { uRhs(i, j, k) -= -vel(i - 1, j, k).z * factorBack; } } // V-terms: vxx + 2vyy + vzz + u_yx + w_yz if (vState(i, j, k) == isFluid) { vRhs(i, j, k) = vVolLiquid(i, j, k) * vel(i, j, k).y; vA0(i, j, k) = vVolLiquid(i, j, k); Real viscRight = 0.25 * (viscosity(i, j - 1, k) + viscosity(i + 1, j - 1, k) + viscosity(i, j, k) + viscosity(i + 1, j, k)); Real viscLeft = 0.25 * (viscosity(i, j - 1, k) + viscosity(i - 1, j - 1, k) + viscosity(i, j, k) + viscosity(i - 1, j, k)); Real volRight = ezVolLiquid(i + 1, j, k); Real volLeft = ezVolLiquid(i, j, k); Real viscTop = viscosity(i, j, k); Real viscBottom = viscosity(i, j - 1, k); Real volTop = cVolLiquid(i, j, k); Real volBottom = cVolLiquid(i, j - 1, k); Real viscFront = 0.25 * (viscosity(i, j - 1, k) + viscosity(i, j - 1, k + 1) + viscosity(i, j, k) + viscosity(i, j, k + 1)); Real viscBack = 0.25 * (viscosity(i, j - 1, k) + viscosity(i, j - 1, k - 1) + viscosity(i, j, k) + viscosity(i, j, k - 1)); Real volFront = exVolLiquid(i, j, k + 1); Real volBack = exVolLiquid(i, j, k); Real factorRight = factor * viscRight * volRight; Real factorLeft = factor * viscLeft * volLeft; Real factorTop = 2 * factor * viscTop * volTop; Real factorBottom = 2 * factor * viscBottom * volBottom; Real factorFront = factor * viscFront * volFront; Real factorBack = factor * viscBack * volBack; // v_x_right vA0(i, j, k) += factorRight; if (vState(i + 1, j, k) == isFluid) { vAplusi(i, j, k) += -factorRight; } else if (vState(i + 1, j, k) == isObstacle) { vRhs(i, j, k) -= -vel(i + 1, j, k).y * factorRight; } // v_x_left vA0(i, j, k) += factorLeft; if (vState(i - 1, j, k) == isFluid) { vAminusi(i, j, k) += -factorLeft; } else if (vState(i - 1, j, k) == isObstacle) { vRhs(i, j, k) -= -vel(i - 1, j, k).y * factorLeft; } // vy_top vA0(i, j, k) += factorTop; if (vState(i, j + 1, k) == isFluid) { vAplusj(i, j, k) += -factorTop; } else if (vState(i, j + 1, k) == isObstacle) { vRhs(i, j, k) -= -vel(i, j + 1, k).y * factorTop; } // vy_bottom vA0(i, j, k) += factorBottom; if (vState(i, j - 1, k) == isFluid) { vAminusj(i, j, k) += -factorBottom; } else if (vState(i, j - 1, k) == isObstacle) { vRhs(i, j, k) -= -vel(i, j - 1, k).y * factorBottom; } // v_z_front vA0(i, j, k) += factorFront; if (vState(i, j, k + 1) == isFluid) { vAplusk(i, j, k) += -factorFront; } else if (vState(i, j, k + 1) == isObstacle) { vRhs(i, j, k) -= -vel(i, j, k + 1).y * factorFront; } // v_z_back vA0(i, j, k) += factorBack; if (vState(i, j, k - 1) == isFluid) { vAminusk(i, j, k) += -factorBack; } else if (vState(i, j, k - 1) == isObstacle) { vRhs(i, j, k) -= -vel(i, j, k - 1).y * factorBack; } // u_y_right if (uState(i + 1, j, k) == isFluid) { vAhelper1(i, j, k) += -factorRight; } else if (uState(i + 1, j, k) == isObstacle) { vRhs(i, j, k) -= -vel(i + 1, j, k).x * factorRight; } if (uState(i + 1, j - 1, k) == isFluid) { vAhelper2(i, j, k) += factorRight; } else if (uState(i + 1, j - 1, k) == isObstacle) { vRhs(i, j, k) -= vel(i + 1, j - 1, k).x * factorRight; } // u_y_left if (uState(i, j, k) == isFluid) { vAhelper3(i, j, k) += factorLeft; } else if (uState(i, j, k) == isObstacle) { vRhs(i, j, k) -= vel(i, j, k).x * factorLeft; } if (uState(i, j - 1, k) == isFluid) { vAhelper4(i, j, k) += -factorLeft; } else if (uState(i, j - 1, k) == isObstacle) { vRhs(i, j, k) -= -vel(i, j - 1, k).x * factorLeft; } // w_y_front if (wState(i, j, k + 1) == isFluid) { vAhelper5(i, j, k) += -factorFront; } else if (wState(i, j, k + 1) == isObstacle) { vRhs(i, j, k) -= -vel(i, j, k + 1).z * factorFront; } if (wState(i, j - 1, k + 1) == isFluid) { vAhelper6(i, j, k) += factorFront; } else if (wState(i, j - 1, k + 1) == isObstacle) { vRhs(i, j, k) -= vel(i, j - 1, k + 1).z * factorFront; } // w_y_back if (wState(i, j, k) == isFluid) { vAhelper7(i, j, k) += factorBack; } else if (wState(i, j, k) == isObstacle) { vRhs(i, j, k) -= vel(i, j, k).z * factorBack; } if (wState(i, j - 1, k) == isFluid) { vAhelper8(i, j, k) += -factorBack; } else if (wState(i, j - 1, k) == isObstacle) { vRhs(i, j, k) -= -vel(i, j - 1, k).z * factorBack; } } // W-terms: wxx+ wyy+ 2wzz + u_zx + v_zy if (wState(i, j, k) == isFluid) { wRhs(i, j, k) = wVolLiquid(i, j, k) * vel(i, j, k).z; wA0(i, j, k) = wVolLiquid(i, j, k); Real viscRight = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + viscosity(i + 1, j, k) + viscosity(i + 1, j, k - 1)); Real viscLeft = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + viscosity(i - 1, j, k) + viscosity(i - 1, j, k - 1)); Real volRight = eyVolLiquid(i + 1, j, k); Real volLeft = eyVolLiquid(i, j, k); Real viscTop = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + viscosity(i, j + 1, k) + viscosity(i, j + 1, k - 1)); Real viscBottom = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + viscosity(i, j - 1, k) + viscosity(i, j - 1, k - 1)); Real volTop = exVolLiquid(i, j + 1, k); Real volBottom = exVolLiquid(i, j, k); Real viscFront = viscosity(i, j, k); Real viscBack = viscosity(i, j, k - 1); Real volFront = cVolLiquid(i, j, k); Real volBack = cVolLiquid(i, j, k - 1); Real factorRight = factor * viscRight * volRight; Real factorLeft = factor * viscLeft * volLeft; Real factorTop = factor * viscTop * volTop; Real factorBottom = factor * viscBottom * volBottom; Real factorFront = 2 * factor * viscFront * volFront; Real factorBack = 2 * factor * viscBack * volBack; // w_x_right wA0(i, j, k) += factorRight; if (wState(i + 1, j, k) == isFluid) { wAplusi(i, j, k) += -factorRight; } else if (wState(i + 1, j, k) == isObstacle) { wRhs(i, j, k) -= -vel(i + 1, j, k).z * factorRight; } // w_x_left wA0(i, j, k) += factorLeft; if (wState(i - 1, j, k) == isFluid) { wAminusi(i, j, k) += -factorLeft; } else if (wState(i - 1, j, k) == isObstacle) { wRhs(i, j, k) -= -vel(i - 1, j, k).z * factorLeft; } // w_y_top wA0(i, j, k) += factorTop; if (wState(i, j + 1, k) == isFluid) { wAplusj(i, j, k) += -factorTop; } else if (wState(i, j + 1, k) == isObstacle) { wRhs(i, j, k) -= -vel(i, j + 1, k).z * factorTop; } // w_y_bottom wA0(i, j, k) += factorBottom; if (wState(i, j - 1, k) == isFluid) { wAminusj(i, j, k) += -factorBottom; } else if (wState(i, j - 1, k) == isObstacle) { wRhs(i, j, k) -= -vel(i, j - 1, k).z * factorBottom; } // w_z_front wA0(i, j, k) += factorFront; if (wState(i, j, k + 1) == isFluid) { wAplusk(i, j, k) += -factorFront; } else if (wState(i, j, k + 1) == isObstacle) { wRhs(i, j, k) -= -vel(i, j, k + 1).z * factorFront; } // w_z_back wA0(i, j, k) += factorBack; if (wState(i, j, k - 1) == isFluid) { wAminusk(i, j, k) += -factorBack; } else if (wState(i, j, k - 1) == isObstacle) { wRhs(i, j, k) -= -vel(i, j, k - 1).z * factorBack; } // u_z_right if (uState(i + 1, j, k) == isFluid) { wAhelper1(i, j, k) += -factorRight; } else if (uState(i + 1, j, k) == isObstacle) { wRhs(i, j, k) -= -vel(i + 1, j, k).x * factorRight; } if (uState(i + 1, j, k - 1) == isFluid) { wAhelper2(i, j, k) += factorRight; } else if (uState(i + 1, j, k - 1) == isObstacle) { wRhs(i, j, k) -= vel(i + 1, j, k - 1).x * factorRight; } // u_z_left if (uState(i, j, k) == isFluid) { wAhelper3(i, j, k) += factorLeft; } else if (uState(i, j, k) == isObstacle) { wRhs(i, j, k) -= vel(i, j, k).x * factorLeft; } if (uState(i, j, k - 1) == isFluid) { wAhelper4(i, j, k) += -factorLeft; } else if (uState(i, j, k - 1) == isObstacle) { wRhs(i, j, k) -= -vel(i, j, k - 1).x * factorLeft; } // v_z_top if (vState(i, j + 1, k) == isFluid) { wAhelper5(i, j, k) += -factorTop; } else if (vState(i, j + 1, k) == isObstacle) { wRhs(i, j, k) -= -vel(i, j + 1, k).y * factorTop; } if (vState(i, j + 1, k - 1) == isFluid) { wAhelper6(i, j, k) += factorTop; } else if (vState(i, j + 1, k - 1) == isObstacle) { wRhs(i, j, k) -= vel(i, j + 1, k - 1).y * factorTop; } // v_z_bottom if (vState(i, j, k) == isFluid) { wAhelper7(i, j, k) += factorBottom; } else if (vState(i, j, k) == isObstacle) { wRhs(i, j, k) -= vel(i, j, k).y * factorBottom; } if (vState(i, j, k - 1) == isFluid) { wAhelper8(i, j, k) += -factorBottom; } else if (vState(i, j, k - 1) == isObstacle) { wRhs(i, j, k) -= -vel(i, j, k - 1).y * factorBottom; } } } // CG solver for U if (flags.is3D()) { vector *> uMatA{&uA0, &uAplusi, &uAplusj, &uAplusk, &uAminusi, &uAminusj, &uAminusk, &uAhelper1, &uAhelper2, &uAhelper3, &uAhelper4, &uAhelper5, &uAhelper6, &uAhelper7, &uAhelper8}; vector *> uVecRhs{&vRhs, &wRhs}; uGcg = new GridCg( uSolution, uRhs, uResidual, uSearch, flags, uTmp, uMatA, uVecRhs); } else { errMsg("Viscosity: 2D Matrix application not yet supported in viscosity solver"); } // CG solver for V if (flags.is3D()) { vector *> vMatA{&vA0, &vAplusi, &vAplusj, &vAplusk, &vAminusi, &vAminusj, &vAminusk, &vAhelper1, &vAhelper2, &vAhelper3, &vAhelper4, &vAhelper5, &vAhelper6, &vAhelper7, &vAhelper8}; vector *> vVecRhs{&uRhs, &wRhs}; vGcg = new GridCg( vSolution, vRhs, vResidual, vSearch, flags, vTmp, vMatA, vVecRhs); } else { errMsg("Viscosity: 2D Matrix application not yet supported in viscosity solver"); } // CG solver for W if (flags.is3D()) { vector *> wMatA{&wA0, &wAplusi, &wAplusj, &wAplusk, &wAminusi, &wAminusj, &wAminusk, &wAhelper1, &wAhelper2, &wAhelper3, &wAhelper4, &wAhelper5, &wAhelper6, &wAhelper7, &wAhelper8}; vector *> wVecRhs{&uRhs, &vRhs}; wGcg = new GridCg( wSolution, wRhs, wResidual, wSearch, flags, wTmp, wMatA, wVecRhs); } else { errMsg("Viscosity: 2D Matrix application not yet supported in viscosity solver"); } // Same accuracy for all dimensions uGcg->setAccuracy(cgAccuracy); vGcg->setAccuracy(cgAccuracy); wGcg->setAccuracy(cgAccuracy); // CG solve. Preconditioning not supported yet. Instead, U, V, W can optionally be solved in // parallel. for (int uIter = 0, vIter = 0, wIter = 0; uIter < maxIter || vIter < maxIter || wIter < maxIter; uIter++, vIter++, wIter++) { #if ENABLE_PARALLEL == 1 parallel_block do_parallel #endif if (uIter < maxIter && !uGcg->iterate()) uIter = maxIter; #if ENABLE_PARALLEL == 1 do_end do_parallel #endif if (vIter < maxIter && !vGcg->iterate()) vIter = maxIter; #if ENABLE_PARALLEL == 1 do_end do_parallel #endif if (wIter < maxIter && !wGcg->iterate()) wIter = maxIter; #if ENABLE_PARALLEL == 1 do_end block_end #endif // Make sure that next CG iteration has updated rhs grids uRhs.copyFrom(uSearch); vRhs.copyFrom(vSearch); wRhs.copyFrom(wSearch); } debMsg( "Viscosity: solveViscosity() done. " "Iterations (u,v,w): (" << uGcg->getIterations() << "," << vGcg->getIterations() << "," << wGcg->getIterations() << "), " "Residual (u,v,w): (" << uGcg->getResNorm() << "," << vGcg->getResNorm() << "," << wGcg->getResNorm() << ")", 2); delete uGcg; delete vGcg; delete wGcg; // Apply solutions to global velocity grid KnApplyVelocities(vel, uState, vState, wState, uSolution, vSolution, wSolution); } //! To use the viscosity plugin, scenes must call this function before solving pressure. //! Note that the 'volumes' grid uses 2x the base resolution void applyViscosity(const FlagGrid &flags, const Grid &phi, MACGrid &vel, Grid &volumes, Grid &viscosity, const Real cgAccuracy = 1e-9, const Real cgMaxIterFac = 1.5) { const Real dx = flags.getParent()->getDx(); const Real dt = flags.getParent()->getDt(); // Reserve temp grids for volume weight calculation FluidSolver *parent = flags.getParent(); Grid cVolLiquid(parent); Grid uVolLiquid(parent); Grid vVolLiquid(parent); Grid wVolLiquid(parent); Grid exVolLiquid(parent); Grid eyVolLiquid(parent); Grid ezVolLiquid(parent); // Ensure final weight grid gets cleared at every step volumes.clear(); // Save viscous fluid volume in double-sized volumes grid computeWeights(phi, volumes, cVolLiquid, uVolLiquid, vVolLiquid, wVolLiquid, exVolLiquid, eyVolLiquid, ezVolLiquid, dx); // Set up A matrix and rhs. Solve with CG. Update velocity grid. solveViscosity(flags, vel, cVolLiquid, uVolLiquid, vVolLiquid, wVolLiquid, exVolLiquid, eyVolLiquid, ezVolLiquid, viscosity, dt, dx, cgAccuracy, cgMaxIterFac); } static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "applyViscosity", !noTiming); PyObject *_retval = nullptr; { ArgLocker _lock; const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); const Grid &phi = *_args.getPtr>("phi", 1, &_lock); MACGrid &vel = *_args.getPtr("vel", 2, &_lock); Grid &volumes = *_args.getPtr>("volumes", 3, &_lock); Grid &viscosity = *_args.getPtr>("viscosity", 4, &_lock); const Real cgAccuracy = _args.getOpt("cgAccuracy", 5, 1e-9, &_lock); const Real cgMaxIterFac = _args.getOpt("cgMaxIterFac", 6, 1.5, &_lock); _retval = getPyNone(); applyViscosity(flags, phi, vel, volumes, viscosity, cgAccuracy, cgMaxIterFac); _args.check(); } pbFinalizePlugin(parent, "applyViscosity", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("applyViscosity", e.what()); return 0; } } static const Pb::Register _RP_applyViscosity("", "applyViscosity", _W_0); extern "C" { void PbRegister_applyViscosity() { KEEP_UNUSED(_RP_applyViscosity); } } } // namespace Manta #if ENABLE_PARALLEL == 1 # undef parallel_block # undef do_parallel # undef do_end # undef block_end # undef parallel_for # undef parallel_end #endif