Program Listing for File FluidModel.cpp

Return to documentation for file (SPlisHSPlasH/FluidModel.cpp)

#include "FluidModel.h"
#include "SPHKernels.h"
#include <iostream>
#include "TimeManager.h"
#include "TimeStep.h"
#include "Utilities/Logger.h"
#include "NeighborhoodSearch.h"
#include "Simulation.h"
#include "EmitterSystem.h"
#include "NonPressureForceBase.h"
#include "XSPH.h"


using namespace SPH;
using namespace GenParam;

int FluidModel::NUM_PARTICLES = -1;
int FluidModel::NUM_REUSED_PARTICLES = -1;
int FluidModel::DENSITY0 = -1;
int FluidModel::DRAG_METHOD = -1;
int FluidModel::SURFACE_TENSION_METHOD = -1;
int FluidModel::VISCOSITY_METHOD = -1;
int FluidModel::VORTICITY_METHOD = -1;
int FluidModel::ELASTICITY_METHOD = -1;


FluidModel::FluidModel() :
    m_masses(),
    m_a(),
    m_v0(),
    m_x0(),
    m_x(),
    m_v(),
    m_density(),
    m_particleId(),
    m_objectId(),
    m_objectId0(),
    m_particleState()
{
    m_density0 = 1000.0;
    m_pointSetIndex = 0;

    m_emitterSystem = new EmitterSystem(this);
    m_viscosity = nullptr;
    m_viscosityMethod = 0;
    m_surfaceTension = nullptr;
    m_surfaceTensionMethod = 0;
    m_vorticityMethod = 0;
    m_vorticity = nullptr;
    m_dragMethod = 0;
    m_drag = nullptr;
    m_dragMethodChanged = nullptr;
    m_surfaceTensionMethodChanged = nullptr;
    m_viscosityMethodChanged = nullptr;
    m_vorticityMethodChanged = nullptr;
    m_elasticityMethod = 0;
    m_elasticity = nullptr;
    m_elasticityMethodChanged = nullptr;
    m_xsph = nullptr;

    addField({ "id", FieldType::UInt, [&](const unsigned int i) -> unsigned int* { return &getParticleId(i); }, true });
    addField({ "state", FieldType::UInt, [&](const unsigned int i) -> unsigned int* { return (unsigned int*)(&m_particleState[i]); }, true });
    addField({ "object_id", FieldType::UInt, [&](const unsigned int i) -> unsigned int* { return &getObjectId(i); }, true });
    addField({ "position", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &getPosition(i)[0]; }, true });
    addField({ "position0", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &getPosition0(i)[0]; } });
    addField({ "velocity", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &getVelocity(i)[0]; }, true });
    addField({ "density", FieldType::Scalar, [&](const unsigned int i) -> Real* { return &getDensity(i); }, false });
}

FluidModel::~FluidModel(void)
{
    removeFieldByName("id");
    removeFieldByName("state");
    removeFieldByName("object_id");
    removeFieldByName("position");
    removeFieldByName("position0");
    removeFieldByName("velocity");
    removeFieldByName("density");

    delete m_xsph;
    delete m_emitterSystem;
    delete m_surfaceTension;
    delete m_drag;
    delete m_vorticity;
    delete m_viscosity;
    delete m_elasticity;

    releaseFluidParticles();
}

void FluidModel::init()
{
    m_xsph = new XSPH(this);
    m_xsph->init();

    initParameters();

    setViscosityMethod(1);
}

void FluidModel::deferredInit()
{
    if (m_xsph)
        m_xsph->deferredInit();
    if (m_surfaceTension)
        m_surfaceTension->deferredInit();
    if (m_viscosity)
        m_viscosity->deferredInit();
    if (m_vorticity)
        m_vorticity->deferredInit();
    if (m_drag)
        m_drag->deferredInit();
    if (m_elasticity)
        m_elasticity->deferredInit();
}


void FluidModel::initParameters()
{
    std::string groupName = std::string("Fluid Model|") + getId();
    ParameterObject::initParameters();

    ParameterBase::GetFunc<Real> getDensity0Fct = std::bind(&FluidModel::getDensity0, this);
    ParameterBase::SetFunc<Real> setDensity0Fct = std::bind(&FluidModel::setDensity0, this, std::placeholders::_1);
    DENSITY0 = createNumericParameter("density0", "Rest density", getDensity0Fct, setDensity0Fct);
    setGroup(DENSITY0, groupName);
    setDescription(DENSITY0, "Rest density of the fluid.");
    getParameter(DENSITY0)->setReadOnly(true);

    NUM_PARTICLES = createNumericParameter("numParticles", "# active particles", &m_numActiveParticles);
    setGroup(NUM_PARTICLES, groupName);
    setDescription(NUM_PARTICLES, "Number of active fluids particles in the simulation.");
    getParameter(NUM_PARTICLES)->setReadOnly(true);

    NUM_REUSED_PARTICLES = createNumericParameter<unsigned int>("numReusedParticles", "# reused particles", [&]() { return m_emitterSystem->numReusedParticles(); });
    setGroup(NUM_REUSED_PARTICLES, groupName);
    setDescription(NUM_REUSED_PARTICLES, "Number of reused fluid particles in the simulation.");
    getParameter(NUM_REUSED_PARTICLES)->setReadOnly(true);

    ParameterBase::GetFunc<int> getDragFct = std::bind(&FluidModel::getDragMethod, this);
    ParameterBase::SetFunc<int> setDragFct = std::bind(static_cast<void (FluidModel::*)(const unsigned int)>(&FluidModel::setDragMethod), this, std::placeholders::_1);
    DRAG_METHOD = createEnumParameter("dragMethod", "Drag method", getDragFct, setDragFct);
    setGroup(DRAG_METHOD, "Fluid Model|Drag force");
    setDescription(DRAG_METHOD, "Method to compute drag forces.");
    EnumParameter *enumParam = static_cast<EnumParameter*>(getParameter(DRAG_METHOD));
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& dragMethods = sim->getDragMethods();
    for (unsigned int i = 0; i < dragMethods.size(); i++)
        enumParam->addEnumValue(dragMethods[i].m_name, dragMethods[i].m_id);

    ParameterBase::GetFunc<int> getElasticityFct = std::bind(&FluidModel::getElasticityMethod, this);
    ParameterBase::SetFunc<int> setElasticityFct = std::bind(static_cast<void (FluidModel::*)(const unsigned int)>(&FluidModel::setElasticityMethod), this, std::placeholders::_1);
    ELASTICITY_METHOD = createEnumParameter("elasticityMethod", "Elasticity method", getElasticityFct, setElasticityFct);
    setGroup(ELASTICITY_METHOD, "Fluid Model|Elasticity");
    setDescription(ELASTICITY_METHOD, "Method to compute elastic forces.");
    enumParam = static_cast<EnumParameter*>(getParameter(ELASTICITY_METHOD));
    std::vector<Simulation::NonPressureForceMethod>& elasticityMethods = sim->getElasticityMethods();
    for (unsigned int i = 0; i < elasticityMethods.size(); i++)
        enumParam->addEnumValue(elasticityMethods[i].m_name, elasticityMethods[i].m_id);

    ParameterBase::GetFunc<int> getSurfaceTensionFct = std::bind(&FluidModel::getSurfaceTensionMethod, this);
    ParameterBase::SetFunc<int> setSurfaceTensionFct = std::bind(static_cast<void (FluidModel::*)(const unsigned int)>(&FluidModel::setSurfaceTensionMethod), this, std::placeholders::_1);
    SURFACE_TENSION_METHOD = createEnumParameter("surfaceTensionMethod", "Surface tension", getSurfaceTensionFct, setSurfaceTensionFct);
    setGroup(SURFACE_TENSION_METHOD, "Fluid Model|Surface tension");
    setDescription(SURFACE_TENSION_METHOD, "Method to compute surface tension forces.");
    enumParam = static_cast<EnumParameter*>(getParameter(SURFACE_TENSION_METHOD));
    std::vector<Simulation::NonPressureForceMethod>& surfaceTensionMethods = sim->getSurfaceTensionMethods();
    for (unsigned int i = 0; i < surfaceTensionMethods.size(); i++)
        enumParam->addEnumValue(surfaceTensionMethods[i].m_name, surfaceTensionMethods[i].m_id);

    ParameterBase::GetFunc<int> getViscosityFct = std::bind(&FluidModel::getViscosityMethod, this);
    ParameterBase::SetFunc<int> setViscosityFct = std::bind(static_cast<void (FluidModel::*)(const unsigned int)>(&FluidModel::setViscosityMethod), this, std::placeholders::_1);
    VISCOSITY_METHOD = createEnumParameter("viscosityMethod", "Viscosity", getViscosityFct, setViscosityFct);
    setGroup(VISCOSITY_METHOD, "Fluid Model|Viscosity");
    setDescription(VISCOSITY_METHOD, "Method to compute viscosity forces.");
    enumParam = static_cast<EnumParameter*>(getParameter(VISCOSITY_METHOD));

    std::vector<Simulation::NonPressureForceMethod>& viscoMethods = sim->getViscosityMethods();
    for (unsigned int i=0; i < viscoMethods.size(); i++)
        enumParam->addEnumValue(viscoMethods[i].m_name, viscoMethods[i].m_id);

    ParameterBase::GetFunc<int> getVorticityFct = std::bind(&FluidModel::getVorticityMethod, this);
    ParameterBase::SetFunc<int> setVorticityFct = std::bind(static_cast<void (FluidModel::*)(const unsigned int)>(&FluidModel::setVorticityMethod), this, std::placeholders::_1);
    VORTICITY_METHOD = createEnumParameter("vorticityMethod", "Vorticity", getVorticityFct, setVorticityFct);
    setGroup(VORTICITY_METHOD, "Fluid Model|Vorticity");
    setDescription(VORTICITY_METHOD, "Method to compute vorticity forces.");
    enumParam = static_cast<EnumParameter*>(getParameter(VORTICITY_METHOD));
    std::vector<Simulation::NonPressureForceMethod>& vorticityMethods = sim->getVorticityMethods();
    for (unsigned int i = 0; i < vorticityMethods.size(); i++)
        enumParam->addEnumValue(vorticityMethods[i].m_name, vorticityMethods[i].m_id);


}


void FluidModel::reset()
{
    setNumActiveParticles(m_numActiveParticles0);
    const unsigned int nPoints = numActiveParticles();

    // use numParticles since numActiveParticles is already reset
    for (unsigned int i = 0; i < numParticles(); i++)
    {
        const Vector3r& x0 = getPosition0(i);
        getPosition(i) = x0;
        getVelocity(i) = getVelocity0(i);
        getAcceleration(i).setZero();
        m_objectId[i] = m_objectId0[i];
        m_density[i] = 0.0;
        m_particleId[i] = i;
        m_particleState[i] = ParticleState::Active;
    }

    NeighborhoodSearch *neighborhoodSearch = Simulation::getCurrent()->getNeighborhoodSearch();
    if (neighborhoodSearch->point_set(m_pointSetIndex).n_points() != nPoints)
        neighborhoodSearch->resize_point_set(m_pointSetIndex, &getPosition(0)[0], nPoints);

    if (m_surfaceTension)
        m_surfaceTension->reset();
    if (m_viscosity)
        m_viscosity->reset();
    if (m_vorticity)
        m_vorticity->reset();
    if (m_drag)
        m_drag->reset();
    if (m_elasticity)
        m_elasticity->reset();
    if (m_xsph)
        m_xsph->reset();

    m_emitterSystem->reset();
}

void FluidModel::initMasses()
{
    const Real particleRadius = Simulation::getCurrent()->getParticleRadius();
    const int nParticles = (int) numParticles();
    const Real diam = static_cast<Real>(2.0)*particleRadius;
    if (Simulation::getCurrent()->is2DSimulation())
        m_V = static_cast<Real>(0.8) * diam*diam;
    else
        m_V = static_cast<Real>(0.8) * diam*diam*diam;

    #pragma omp parallel default(shared)
    {
        #pragma omp for schedule(static)
        for (int i = 0; i < nParticles; i++)
        {
            setMass(i, m_V * m_density0);                       // each particle represents a cube with a side length of r
                                                                // mass is slightly reduced to prevent pressure at the beginning of the simulation
        }
    }
}

void FluidModel::resizeFluidParticles(const unsigned int newSize)
{
    m_x0.resize(newSize);
    m_x.resize(newSize);
    m_v.resize(newSize);
    m_v0.resize(newSize);
    m_a.resize(newSize);
    m_masses.resize(newSize);
    m_density.resize(newSize);
    m_particleId.resize(newSize);
    m_objectId.resize(newSize);
    m_objectId0.resize(newSize);
    m_particleState.resize(newSize, ParticleState::Active);
}

void FluidModel::releaseFluidParticles()
{
    m_x0.clear();
    m_x.clear();
    m_v.clear();
    m_v0.clear();
    m_a.clear();
    m_masses.clear();
    m_density.clear();
    m_particleId.clear();
    m_objectId.clear();
    m_objectId0.clear();
    m_particleState.clear();
}

void FluidModel::initModel(const std::string &id, const unsigned int nFluidParticles, Vector3r* fluidParticles, Vector3r* fluidVelocities, unsigned int* fluidObjectIds, const unsigned int nMaxEmitterParticles)
{
    m_id = id;
    init();
    releaseFluidParticles();
    resizeFluidParticles(nFluidParticles + nMaxEmitterParticles);

    // copy fluid positions
    #pragma omp parallel default(shared)
    {
        #pragma omp for schedule(static)
        for (int i = 0; i < (int)nFluidParticles; i++)
        {
            getPosition0(i) = fluidParticles[i];
            getPosition(i) = fluidParticles[i];
            getVelocity0(i) = fluidVelocities[i];
            getVelocity(i) = fluidVelocities[i];
            getAcceleration(i).setZero();
            m_density[i] = 0.0;
            m_particleId[i] = i;
            m_objectId[i] = fluidObjectIds[i];
            m_objectId0[i] = fluidObjectIds[i];
            if (m_particleState[i] != ParticleState::Fixed)
                m_particleState[i] = ParticleState::Active;
        }
    }
    // set IDs for emitted particles
    for (unsigned int i = nFluidParticles; i < (nFluidParticles + nMaxEmitterParticles); i++)
    {
        m_particleId[i] = i;
        m_objectId[i] = 0;
    }

    // initialize masses
    initMasses();

    // Fluids
    NeighborhoodSearch *neighborhoodSearch = Simulation::getCurrent()->getNeighborhoodSearch();
    m_pointSetIndex = neighborhoodSearch->add_point_set(&getPosition(0)[0], nFluidParticles, true, true, true, this);

    m_numActiveParticles0 = nFluidParticles;
    m_numActiveParticles = m_numActiveParticles0;
}


void FluidModel::performNeighborhoodSearchSort()
{
    const unsigned int numPart = numActiveParticles();
    if (numPart == 0)
        return;

    NeighborhoodSearch *neighborhoodSearch = Simulation::getCurrent()->getNeighborhoodSearch();

    auto const& d = neighborhoodSearch->point_set(m_pointSetIndex);
    d.sort_field(&m_x[0]);
    d.sort_field(&m_v[0]);
    d.sort_field(&m_a[0]);
    d.sort_field(&m_masses[0]);
    d.sort_field(&m_density[0]);
    d.sort_field(&m_particleId[0]);
    d.sort_field(&m_objectId[0]);
    d.sort_field(&m_particleState[0]);

    if (m_viscosity)
        m_viscosity->performNeighborhoodSearchSort();
    if (m_surfaceTension)
        m_surfaceTension->performNeighborhoodSearchSort();
    if (m_vorticity)
        m_vorticity->performNeighborhoodSearchSort();
    if (m_drag)
        m_drag->performNeighborhoodSearchSort();
    if (m_elasticity)
        m_elasticity->performNeighborhoodSearchSort();
    if (m_xsph)
        m_xsph->performNeighborhoodSearchSort();
}

void SPH::FluidModel::setDensity0(const Real v)
{
    m_density0 = v;
    initMasses();
}

const SPH::FieldDescription & SPH::FluidModel::getField(const std::string &name)
{
    unsigned int index = 0;
    for (auto i = 0; i < m_fields.size(); i++)
    {
        if (m_fields[i].name == name)
        {
            index = i;
            break;
        }
    }
    return m_fields[index];
}

void FluidModel::setNumActiveParticles(const unsigned int num)
{
    m_numActiveParticles = num;
}

unsigned int FluidModel::numActiveParticles() const
{
    return m_numActiveParticles;
}

void FluidModel::setDragMethodChangedCallback(std::function<void()> const& callBackFct)
{
    m_dragMethodChanged = callBackFct;
}

void FluidModel::setSurfaceMethodChangedCallback(std::function<void()> const& callBackFct)
{
    m_surfaceTensionMethodChanged = callBackFct;
}

void FluidModel::setViscosityMethodChangedCallback(std::function<void()> const& callBackFct)
{
    m_viscosityMethodChanged = callBackFct;
}

void FluidModel::setVorticityMethodChangedCallback(std::function<void()> const& callBackFct)
{
    m_vorticityMethodChanged = callBackFct;
}

void FluidModel::setElasticityMethodChangedCallback(std::function<void()> const& callBackFct)
{
    m_elasticityMethodChanged = callBackFct;
}

void FluidModel::computeSurfaceTension()
{
    if (m_surfaceTension)
        m_surfaceTension->step();
}

void FluidModel::computeViscosity()
{
    if (m_viscosity)
        m_viscosity->step();
}

void FluidModel::computeVorticity()
{
    if (m_vorticity)
        m_vorticity->step();
}

void FluidModel::computeDragForce()
{
    if (m_drag)
        m_drag->step();
}

void FluidModel::computeElasticity()
{
    if (m_elasticity)
        m_elasticity->step();
}

void FluidModel::computeXSPH()
{
    if (m_xsph)
        m_xsph->step();
}

void FluidModel::emittedParticles(const unsigned int startIndex)
{
    if (m_viscosity)
        m_viscosity->emittedParticles(startIndex);
    if (m_surfaceTension)
        m_surfaceTension->emittedParticles(startIndex);
    if (m_vorticity)
        m_vorticity->emittedParticles(startIndex);
    if (m_drag)
        m_drag->emittedParticles(startIndex);
    if (m_elasticity)
        m_elasticity->emittedParticles(startIndex);
    if (m_xsph)
        m_xsph->emittedParticles(startIndex);
}

void FluidModel::setSurfaceTensionMethod(const std::string& val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& methods = sim->getSurfaceTensionMethods();
    for (size_t i = 0; i < methods.size(); i++)
    {
        if (methods[i].m_name == val)
        {
            setSurfaceTensionMethod(static_cast<unsigned int>(i));
            break;
        }
    }
}

void FluidModel::setSurfaceTensionMethod(const unsigned int val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& stMethods = sim->getSurfaceTensionMethods();

    unsigned int stm = val;
    if (stm >= stMethods.size())
        stm = 0;

    if (stm == m_surfaceTensionMethod)
        return;

    delete m_surfaceTension;
    m_surfaceTension = nullptr;

    m_surfaceTensionMethod = stm;
    for (unsigned int i = 0; i < stMethods.size(); i++)
    {
        if (stMethods[i].m_id == m_surfaceTensionMethod)
        {
            m_surfaceTension = static_cast<NonPressureForceBase*>(stMethods[i].m_creator(this));
            break;
        }
    }

    if (m_surfaceTension != nullptr)
        m_surfaceTension->init();

    if (m_surfaceTensionMethodChanged != nullptr)
        m_surfaceTensionMethodChanged();
}

void FluidModel::setViscosityMethod(const std::string &val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& methods = sim->getViscosityMethods();
    for (size_t i = 0; i < methods.size(); i++)
    {
        if (methods[i].m_name == val)
        {
            setViscosityMethod(static_cast<unsigned int>(i));
            break;
        }
    }
}

void FluidModel::setViscosityMethod(const unsigned int val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& viscoMethods = sim->getViscosityMethods();

    unsigned int vm = val;
    if (vm >= viscoMethods.size())
        vm = 0;

    if (vm == m_viscosityMethod)
        return;

    delete m_viscosity;
    m_viscosity = nullptr;

    m_viscosityMethod = vm;

    for (unsigned int i = 0; i < viscoMethods.size(); i++)
    {
        if (viscoMethods[i].m_id == m_viscosityMethod)
        {
            m_viscosity = static_cast<NonPressureForceBase*>(viscoMethods[i].m_creator(this));
            break;
        }
    }

    if (m_viscosity != nullptr)
        m_viscosity->init();

    if (m_viscosityMethodChanged != nullptr)
        m_viscosityMethodChanged();
}

void FluidModel::setVorticityMethod(const std::string& val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& methods = sim->getVorticityMethods();
    for (size_t i = 0; i < methods.size(); i++)
    {
        if (methods[i].m_name == val)
        {
            setVorticityMethod(static_cast<unsigned int>(i));
            break;
        }
    }
}

void FluidModel::setVorticityMethod(const unsigned int val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& vorticityMethods = sim->getVorticityMethods();

    unsigned int vm = val;
    if (vm >= vorticityMethods.size())
        vm = 0;

    if (vm == m_vorticityMethod)
        return;

    delete m_vorticity;
    m_vorticity = nullptr;

    m_vorticityMethod = vm;

    for (unsigned int i = 0; i < vorticityMethods.size(); i++)
    {
        if (vorticityMethods[i].m_id == m_vorticityMethod)
            m_vorticity = static_cast<NonPressureForceBase*>(vorticityMethods[i].m_creator(this));
    }

    if (m_vorticity != nullptr)
        m_vorticity->init();

    if (m_vorticityMethodChanged != nullptr)
        m_vorticityMethodChanged();
}

void FluidModel::setDragMethod(const std::string& val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& methods = sim->getDragMethods();
    for (size_t i = 0; i < methods.size(); i++)
    {
        if (methods[i].m_name == val)
        {
            setDragMethod(static_cast<unsigned int>(i));
            break;
        }
    }
}

void FluidModel::setDragMethod(const unsigned int val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& dragMethods = sim->getDragMethods();

    unsigned int dm = val;
    if (dm >= dragMethods.size())
        dm = 0;

    if (dm == m_dragMethod)
        return;

    delete m_drag;
    m_drag = nullptr;

    m_dragMethod = dm;

    for (unsigned int i = 0; i < dragMethods.size(); i++)
    {
        if (dragMethods[i].m_id == m_dragMethod)
            m_drag = static_cast<NonPressureForceBase*>(dragMethods[i].m_creator(this));
    }

    if (m_drag != nullptr)
        m_drag->init();

    if (m_dragMethodChanged != nullptr)
        m_dragMethodChanged();
}

void FluidModel::setElasticityMethod(const std::string& val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& methods = sim->getElasticityMethods();
    for (size_t i = 0; i < methods.size(); i++)
    {
        if (methods[i].m_name == val)
        {
            setElasticityMethod(static_cast<unsigned int>(i));
            break;
        }
    }
}

void FluidModel::setElasticityMethod(const unsigned int val)
{
    Simulation* sim = Simulation::getCurrent();
    std::vector<Simulation::NonPressureForceMethod>& elasticityMethods = sim->getElasticityMethods();

    unsigned int em = val;
    if (em >= elasticityMethods.size())
        em = 0;

    if (em == m_elasticityMethod)
        return;

    delete m_elasticity;
    m_elasticity = nullptr;

    m_elasticityMethod = em;
    for (unsigned int i = 0; i < elasticityMethods.size(); i++)
    {
        if (elasticityMethods[i].m_id == m_elasticityMethod)
            m_elasticity = static_cast<NonPressureForceBase*>(elasticityMethods[i].m_creator(this));
    }

    if (m_elasticity != nullptr)
        m_elasticity->init();

    if (m_elasticityMethodChanged != nullptr)
        m_elasticityMethodChanged();
}


void FluidModel::addField(const FieldDescription &field)
{
    m_fields.push_back(field);
    std::sort(m_fields.begin(), m_fields.end(), [](FieldDescription& i, FieldDescription& j) -> bool { if (i.methodName == j.methodName) return (i.name < j.name); else return (i.methodName < j.methodName); });
}

void FluidModel::removeFieldByName(const std::string &fieldName)
{
    for (auto it = m_fields.begin(); it != m_fields.end(); it++)
    {
        if (it->name == fieldName)
        {
            m_fields.erase(it);
            break;
        }
    }
}

void SPH::FluidModel::saveState(BinaryFileWriter &binWriter)
{
    binWriter.write(m_numActiveParticles);
    binWriter.writeBuffer((char*) m_particleState.data(), m_numActiveParticles * sizeof(ParticleState));

    if (m_surfaceTension)
        m_surfaceTension->saveState(binWriter);
    if (m_viscosity)
        m_viscosity->saveState(binWriter);
    if (m_vorticity)
        m_vorticity->saveState(binWriter);
    if (m_drag)
        m_drag->saveState(binWriter);
    if (m_elasticity)
        m_elasticity->saveState(binWriter);
    if (m_xsph)
        m_xsph->saveState(binWriter);
    m_emitterSystem->saveState(binWriter);
}

void SPH::FluidModel::loadState(BinaryFileReader &binReader)
{
    binReader.read(m_numActiveParticles);
    NeighborhoodSearch *neighborhoodSearch = Simulation::getCurrent()->getNeighborhoodSearch();
    neighborhoodSearch->update_point_sets();
    neighborhoodSearch->resize_point_set(m_pointSetIndex, &getPosition(0)[0], m_numActiveParticles);

    binReader.readBuffer((char*)m_particleState.data(), m_numActiveParticles * sizeof(ParticleState));

    if (m_surfaceTension)
        m_surfaceTension->loadState(binReader);
    if (m_viscosity)
        m_viscosity->loadState(binReader);
    if (m_vorticity)
        m_vorticity->loadState(binReader);
    if (m_drag)
        m_drag->loadState(binReader);
    if (m_elasticity)
        m_elasticity->loadState(binReader);
    if (m_xsph)
        m_xsph->loadState(binReader);

    m_emitterSystem->loadState(binReader);
}