Program Listing for File Viscosity_Bender2017.cpp

Return to documentation for file (SPlisHSPlasH/Viscosity/Viscosity_Bender2017.cpp)

#include "Viscosity_Bender2017.h"
#include "SPlisHSPlasH/TimeManager.h"
#include "Utilities/Counting.h"
#include "../Simulation.h"
#include "SPlisHSPlasH/BoundaryModel_Akinci2012.h"
#include "SPlisHSPlasH/BoundaryModel_Koschier2017.h"
#include "SPlisHSPlasH/BoundaryModel_Bender2019.h"

using namespace SPH;
using namespace GenParam;

int Viscosity_Bender2017::ITERATIONS = -1;
int Viscosity_Bender2017::MAX_ITERATIONS = -1;
int Viscosity_Bender2017::MAX_ERROR = -1;


Viscosity_Bender2017::Viscosity_Bender2017(FluidModel *model) :
    ViscosityBase(model)
{
    m_targetStrainRate.resize(model->numParticles(), Vector6r::Zero());
    m_viscosityFactor.resize(model->numParticles(), Matrix6r::Zero());
    m_viscosityLambda.resize(model->numParticles(), Vector6r::Zero());

    m_iterations = 0;
    m_maxIter = 50;
    m_maxError = 0.01;

    model->addField({ "target strain rate", FieldType::Vector6, [&](const unsigned int i) -> Real* { return &m_targetStrainRate[i][0]; } });
    model->addField({ "viscosity factor", FieldType::Matrix6, [&](const unsigned int i) -> Real* { return &m_viscosityFactor[i](0,0); } });
    model->addField({ "viscosity lambda", FieldType::Vector6, [&](const unsigned int i) -> Real* { return &m_viscosityLambda[i][0]; } });
}

Viscosity_Bender2017::~Viscosity_Bender2017(void)
{
    m_model->removeFieldByName("target strain rate");
    m_model->removeFieldByName("viscosity factor");
    m_model->removeFieldByName("viscosity lambda");

    m_targetStrainRate.clear();
    m_viscosityFactor.clear();
    m_viscosityLambda.clear();
}

void Viscosity_Bender2017::initParameters()
{
    ViscosityBase::initParameters();

    ITERATIONS = createNumericParameter("viscoIterations", "Iterations", &m_iterations);
    setGroup(ITERATIONS, "Fluid Model|Viscosity");
    setDescription(ITERATIONS, "Iterations required by the viscosity solver.");
    getParameter(ITERATIONS)->setReadOnly(true);

    MAX_ITERATIONS = createNumericParameter("viscoMaxIter", "Max. iterations (visco)", &m_maxIter);
    setGroup(MAX_ITERATIONS, "Fluid Model|Viscosity");
    setDescription(MAX_ITERATIONS, "Max. iterations of the viscosity solver.");
    static_cast<NumericParameter<unsigned int>*>(getParameter(MAX_ITERATIONS))->setMinValue(1);

    MAX_ERROR = createNumericParameter("viscoMaxError", "Max. visco error", &m_maxError);
    setGroup(MAX_ERROR, "Fluid Model|Viscosity");
    setDescription(MAX_ERROR, "Max. error of the viscosity solver.");
    RealParameter* rparam = static_cast<RealParameter*>(getParameter(MAX_ERROR));
    rparam->setMinValue(1e-6);
}

void Viscosity_Bender2017::step()
{
    Simulation *sim = Simulation::getCurrent();
    const int numParticles = (int) m_model->numActiveParticles();
    if (numParticles == 0)
        return;

    const unsigned int nFluids = sim->numberOfFluidModels();
    const unsigned int nBoundaries = sim->numberOfBoundaryModels();
    FluidModel *model = m_model;
    const unsigned int fluidModelIndex = model->getPointSetIndex();
    const unsigned int maxIter = m_maxIter;
    const Real maxError = m_maxError;
    const Real maxError2 = maxError*maxError;
    const Real density0 = model->getDensity0();

    const Real h = TimeManager::getCurrent()->getTimeStepSize();

    // Compute factors
    computeViscosityFactor();
    computeTargetStrainRate();

    m_iterations = 0;
    while (m_iterations < maxIter)
    {
        // Compute viscosity constraint value
        #pragma omp parallel default(shared)
        {
            #pragma omp for schedule(static) nowait
            for (int i = 0; i < numParticles; i++)
            {
                const Vector3r &xi = m_model->getPosition(i);
                const Vector3r &vi = m_model->getVelocity(i);
                const Real density_i = m_model->getDensity(i);

                Vector6r viscosityC;
                viscosityC.setZero();

                // Fluid
                forall_fluid_neighbors_in_same_phase(
                    const Vector3r &vj = m_model->getVelocity(neighborIndex);
                    const Vector3r gradW = sim->gradW(xi - xj);
                    const Vector3r vji = vj - vi;

                    const Real m = m_model->getMass(neighborIndex);
                    const Real m2 = m * static_cast<Real>(2.0);
                    viscosityC[0] += m2 * vji[0] * gradW[0];
                    viscosityC[1] += m2 * vji[1] * gradW[1];
                    viscosityC[2] += m2 * vji[2] * gradW[2];
                    viscosityC[3] += m * (vji[0] * gradW[1] + vji[1] * gradW[0]);
                    viscosityC[4] += m * (vji[0] * gradW[2] + vji[2] * gradW[0]);
                    viscosityC[5] += m * (vji[1] * gradW[2] + vji[2] * gradW[1]);
                )

                viscosityC = (0.5 / density_i) * viscosityC - getTargetStrainRate(i);

                getViscosityLambda(i) = viscosityC;
            }
        }

        Real avgStrainRateError = 0.0;
        for (int i = 0; i < (int)numParticles; i++)
            for (unsigned int j = 0; j < 6; j++)
                avgStrainRateError += fabs(getViscosityLambda(i)[j]);
        avgStrainRateError = (static_cast<Real>(1.0) / (static_cast<Real>(6.0) *(Real)numParticles)) * avgStrainRateError;

        // Compute viscosity constraint value
        #pragma omp parallel default(shared)
        {
            #pragma omp for schedule(static) nowait
            for (int i = 0; i < (int)numParticles; i++)
            {
                getViscosityLambda(i) = -getViscosityFactor(i) * getViscosityLambda(i);
            }
        }


        // Apply impulses
        #pragma omp parallel default(shared)
        {
            #pragma omp for schedule(static) nowait
            for (int i = 0; i < numParticles; i++)
            {
                const Vector3r &xi = m_model->getPosition(i);
                Vector3r &vi = m_model->getVelocity(i);
                const Real density_i = m_model->getDensity(i);


                // Fluid
                Eigen::Matrix<Real, 3, 6> gradT;
                forall_fluid_neighbors_in_same_phase(
                    const Vector3r gradW = sim->gradW(xi - xj);
                    const Real density_j = m_model->getDensity(neighborIndex);

                    gradT.setZero();
                    gradT(0,0) = static_cast<Real>(2.0) * gradW[0];
                    gradT(0,3) = gradW[1];
                    gradT(0,4) = gradW[2];

                    gradT(1,1) = static_cast<Real>(2.0) * gradW[1];
                    gradT(1,3) = gradW[0];
                    gradT(1,5) = gradW[2];

                    gradT(2,2) = static_cast<Real>(2.0) * gradW[2];
                    gradT(2,4) = gradW[0];
                    gradT(2,5) = gradW[1];

                    vi -= m_model->getMass(neighborIndex)* 0.5 * gradT * ((m_model->getMass(neighborIndex) / (density_i*density_i)) * getViscosityLambda(i) +
                        (m_model->getMass(neighborIndex) / (density_j*density_j)) * getViscosityLambda(neighborIndex));
                )
            }
        }
        m_iterations++;

        if (avgStrainRateError < maxError)
            break;

    }
    INCREASE_COUNTER("Visco iterations", static_cast<Real>(m_iterations));

    // Compute viscosity forces (XSPH) with boundary to simulate simple friction
    const Real invH = (static_cast<Real>(1.0) / h);
    #pragma omp parallel default(shared)
    {
        #pragma omp for schedule(static)
        for (int i = 0; i < (int)numParticles; i++)
        {
            const Vector3r &xi = m_model->getPosition(i);
            const Vector3r &vi = m_model->getVelocity(i);
            Vector3r &ai = m_model->getAcceleration(i);
            const Real density_i = m_model->getDensity(i);

            // Boundary
            if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012)
            {
                forall_boundary_neighbors(
                    const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex);
                    const Vector3r a = -invH * 0.1 * m_viscosity * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) * (vi - vj)* sim->W(xi - xj);
                    ai += a;
                    bm_neighbor->addForce(xj, -m_model->getMass(i) * a);
                );
            }
            else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017)
            {
                forall_density_maps(
                    Vector3r vj;
                    bm_neighbor->getPointVelocity(xi, vj);
                    const Vector3r a = -invH * 0.1 * m_viscosity * (density0 / density_i) * (vi-vj)* rho;
                    ai += a;
                    bm_neighbor->addForce(xj, -m_model->getMass(i) * a);
                );
            }
            else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019)
            {
                forall_volume_maps(
                    Vector3r vj;
                    bm_neighbor->getPointVelocity(xj, vj);
                    const Vector3r a = -invH * 0.1 * m_viscosity * (density0 * Vj / density_i) * (vi-vj)* sim->W(xi - xj);
                    ai += a;
                    bm_neighbor->addForce(xj, -m_model->getMass(i) * a);
                );
            }
        }
    }
}


void Viscosity_Bender2017::reset()
{
}


void Viscosity_Bender2017::computeViscosityFactor()
{
    // Init parameters

    Simulation *sim = Simulation::getCurrent();
    const int numParticles = (int) m_model->numActiveParticles();
    const unsigned int nFluids = sim->numberOfFluidModels();
    const FluidModel *model = m_model;
    const unsigned int fluidModelIndex = model->getPointSetIndex();

    #pragma omp parallel default(shared)
    {
        // Compute inverted viscosity matrix

        #pragma omp for schedule(static)
        for (int i = 0; i < numParticles; i++)
        {
            // Compute viscosity matrix
            const Vector3r &xi = m_model->getPosition(i);
            const Real density_i = m_model->getDensity(i);
            Matrix6r &Kinv = getViscosityFactor(i);
            Matrix6r K;
            K.setZero();

            Eigen::Matrix<Real, 6, 3> grad_i;
            grad_i.setZero();

            // Fluid
            Eigen::Matrix<Real, 6, 3> grad_j;
            forall_fluid_neighbors_in_same_phase(
                const Vector3r gradW = sim->gradW(xi - xj);

                grad_j.setZero();
                grad_j(0,0) = static_cast<Real>(2.0) * gradW[0];
                grad_j(3,0) = gradW[1];
                grad_j(4,0) = gradW[2];

                grad_j(1,1) = static_cast<Real>(2.0) * gradW[1];
                grad_j(3,1) = gradW[0];
                grad_j(5,1) = gradW[2];

                grad_j(2,2) = static_cast<Real>(2.0) * gradW[2];
                grad_j(4,2) = gradW[0];
                grad_j(5,2) = gradW[1];

                grad_j = ((0.5 / density_i) * m_model->getMass(neighborIndex)) * grad_j;
                grad_i -= grad_j;

                Matrix6r Klocal;
                viscoGradientMultTransposeRightOpt((1.0 / m_model->getDensity(i)) * grad_j, grad_j, Klocal);
                K += Klocal;
            )


            Matrix6r Klocal;
            viscoGradientMultTransposeRightOpt((1.0 / m_model->getDensity(i)) * grad_i, grad_i, Klocal);
            K += Klocal;

            Vector6r Kdiag_inv;
            for (unsigned l = 0; l < 6; l++)
            {

                if (fabs(K(l,l)) < 1.0e-6)
                    Kdiag_inv[l] = 1.0;
                else
                    Kdiag_inv[l] = static_cast<Real>(1.0) / K(l,l);
            }
            Matrix6r precondK;
            for (unsigned k = 0; k < 6; k++)
            {
                for (unsigned l = 0; l < 6; l++)
                {
                    precondK(k,l) = Kdiag_inv[k] * K(k,l);
                }
            }

            if (fabs(precondK.determinant()) < 1.0e-6)
                Kinv.setZero();
            else
                Kinv = precondK.inverse();

            for (unsigned k = 0; k < 6; k++)
            {
                for (unsigned l = 0; l < 6; l++)
                {
                    Kinv(k,l) = Kinv(k,l) * Kdiag_inv[l];
                }
            }
        }
    }
}

void Viscosity_Bender2017::computeTargetStrainRate()
{
    Simulation *sim = Simulation::getCurrent();
    const int numParticles = (int) m_model->numActiveParticles();
    const unsigned int fluidModelIndex = m_model->getPointSetIndex();
    const unsigned int nFluids = sim->numberOfFluidModels();
    FluidModel *model = m_model;

    // Compute target strain rate
    #pragma omp parallel default(shared)
    {
        #pragma omp for schedule(static) nowait
        for (int i = 0; i < numParticles; i++)
        {
            const Vector3r &xi = m_model->getPosition(i);
            const Vector3r &vi = m_model->getVelocity(i);
            const Real density_i = m_model->getDensity(i);

            Vector6r &strainRate = getTargetStrainRate(i);
            strainRate.setZero();

            // Fluid
            forall_fluid_neighbors_in_same_phase(
                const Vector3r &vj = m_model->getVelocity(neighborIndex);

                const Vector3r gradW = sim->gradW(xi - xj);
                const Vector3r vji = vj - vi;
                const Real m = m_model->getMass(neighborIndex);
                const Real m2 = m * static_cast<Real>(2.0);
                strainRate[0] += (static_cast<Real>(1.0)-m_viscosity) * m2 * vji[0] * gradW[0];
                strainRate[1] += (static_cast<Real>(1.0)-m_viscosity) * m2 * vji[1] * gradW[1];
                strainRate[2] += (static_cast<Real>(1.0)-m_viscosity) * m2 * vji[2] * gradW[2];
                strainRate[3] += (static_cast<Real>(1.0)-m_viscosity) * m * (vji[0] * gradW[1] + vji[1] * gradW[0]);
                strainRate[4] += (static_cast<Real>(1.0)-m_viscosity) * m * (vji[0] * gradW[2] + vji[2] * gradW[0]);
                strainRate[5] += (static_cast<Real>(1.0)-m_viscosity) * m * (vji[1] * gradW[2] + vji[2] * gradW[1]);
            )
            strainRate = (static_cast<Real>(0.5) / density_i) * strainRate;
        }
    }
}

void Viscosity_Bender2017::performNeighborhoodSearchSort()
{
    const unsigned int numPart = m_model->numActiveParticles();
    if (numPart == 0)
        return;

    Simulation *sim = Simulation::getCurrent();
    auto const& d = sim->getNeighborhoodSearch()->point_set(m_model->getPointSetIndex());
    d.sort_field(&m_targetStrainRate[0]);
    d.sort_field(&m_viscosityFactor[0]);
    d.sort_field(&m_viscosityLambda[0]);
}