Program Listing for File TimeStepController.cpp

Return to documentation for file (Simulation/TimeStepController.cpp)

#include "TimeStepController.h"
#include "Simulation/TimeManager.h"
#include "PositionBasedDynamics/PositionBasedRigidBodyDynamics.h"
#include "PositionBasedDynamics/TimeIntegration.h"
#include <iostream>
#include "PositionBasedDynamics/PositionBasedDynamics.h"
#include "Utils/Timing.h"

using namespace PBD;
using namespace std;
using namespace GenParam;

// int TimeStepController::SOLVER_ITERATIONS = -1;
// int TimeStepController::SOLVER_ITERATIONS_V = -1;
int TimeStepController::NUM_SUB_STEPS = -1;
int TimeStepController::MAX_ITERATIONS = -1;
int TimeStepController::MAX_ITERATIONS_V = -1;
int TimeStepController::VELOCITY_UPDATE_METHOD = -1;
int TimeStepController::ENUM_VUPDATE_FIRST_ORDER = -1;
int TimeStepController::ENUM_VUPDATE_SECOND_ORDER = -1;


TimeStepController::TimeStepController()
{
    m_velocityUpdateMethod = 0;
    m_iterations = 0;
    m_iterationsV = 0;
    m_maxIterations = 1;
    m_maxIterationsV = 5;
    m_subSteps = 5;
    m_collisionDetection = NULL;
}

TimeStepController::~TimeStepController(void)
{
}

void TimeStepController::initParameters()
{
    TimeStep::initParameters();

//  SOLVER_ITERATIONS = createNumericParameter("iterations", "Iterations", &m_iterations);
//  setGroup(SOLVER_ITERATIONS, "Simulation|PBD");
//  setDescription(SOLVER_ITERATIONS, "Iterations required by the solver.");
//  getParameter(SOLVER_ITERATIONS)->setReadOnly(true);

    NUM_SUB_STEPS = createNumericParameter("subSteps", "# sub steps", &m_subSteps);
    setGroup(NUM_SUB_STEPS, "Simulation|PBD");
    setDescription(NUM_SUB_STEPS, "Number of sub steps of the solver.");
    static_cast<NumericParameter<unsigned int>*>(getParameter(NUM_SUB_STEPS))->setMinValue(1);

    MAX_ITERATIONS = createNumericParameter("maxIterations", "Max. iterations", &m_maxIterations);
    setGroup(MAX_ITERATIONS, "Simulation|PBD");
    setDescription(MAX_ITERATIONS, "Maximal number of iterations of the solver.");
    static_cast<NumericParameter<unsigned int>*>(getParameter(MAX_ITERATIONS))->setMinValue(1);

//  SOLVER_ITERATIONS_V = createNumericParameter("iterationsV", "Velocity iterations", &m_iterationsV);
//  setGroup(SOLVER_ITERATIONS_V, "Simulation|PBD");
//  setDescription(SOLVER_ITERATIONS_V, "Iterations required by the velocity solver.");
//  getParameter(SOLVER_ITERATIONS_V)->setReadOnly(true);

    MAX_ITERATIONS_V = createNumericParameter("maxIterationsV", "Max. velocity iterations", &m_maxIterationsV);
    setGroup(MAX_ITERATIONS_V, "Simulation|PBD");
    setDescription(MAX_ITERATIONS_V, "Maximal number of iterations of the velocity solver.");
    static_cast<NumericParameter<unsigned int>*>(getParameter(MAX_ITERATIONS_V))->setMinValue(0);

    VELOCITY_UPDATE_METHOD = createEnumParameter("velocityUpdateMethod", "Velocity update method", &m_velocityUpdateMethod);
    setGroup(VELOCITY_UPDATE_METHOD, "Simulation|PBD");
    setDescription(VELOCITY_UPDATE_METHOD, "Velocity method.");
    EnumParameter* enumParam = static_cast<EnumParameter*>(getParameter(VELOCITY_UPDATE_METHOD));
    enumParam->addEnumValue("First Order Update", ENUM_VUPDATE_FIRST_ORDER);
    enumParam->addEnumValue("Second Order Update", ENUM_VUPDATE_SECOND_ORDER);
}

void TimeStepController::step(SimulationModel &model)
{
    START_TIMING("simulation step");
    TimeManager *tm = TimeManager::getCurrent ();
    const Real hOld = tm->getTimeStepSize();

    // rigid body model
    clearAccelerations(model);
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    ParticleData &pd = model.getParticles();
    OrientationData &od = model.getOrientations();

    const int numBodies = (int)rb.size();

    Real h = hOld / (Real)m_subSteps;
    tm->setTimeStepSize(h);
    for (unsigned int step = 0; step < m_subSteps; step++)
    {
        #pragma omp parallel if(numBodies > MIN_PARALLEL_SIZE) default(shared)
        {
            #pragma omp for schedule(static) nowait
            for (int i = 0; i < numBodies; i++)
            {
                rb[i]->getLastPosition() = rb[i]->getOldPosition();
                rb[i]->getOldPosition() = rb[i]->getPosition();
                TimeIntegration::semiImplicitEuler(h, rb[i]->getMass(), rb[i]->getPosition(), rb[i]->getVelocity(), rb[i]->getAcceleration());
                rb[i]->getLastRotation() = rb[i]->getOldRotation();
                rb[i]->getOldRotation() = rb[i]->getRotation();
                TimeIntegration::semiImplicitEulerRotation(h, rb[i]->getMass(), rb[i]->getInertiaTensorW(), rb[i]->getInertiaTensorInverseW(), rb[i]->getRotation(), rb[i]->getAngularVelocity(), rb[i]->getTorque());
                rb[i]->rotationUpdated();
            }

            // particle model
            #pragma omp for schedule(static)
            for (int i = 0; i < (int) pd.size(); i++)
            {
                pd.getLastPosition(i) = pd.getOldPosition(i);
                pd.getOldPosition(i) = pd.getPosition(i);
                TimeIntegration::semiImplicitEuler(h, pd.getMass(i), pd.getPosition(i), pd.getVelocity(i), pd.getAcceleration(i));
            }

            // orientation model
            #pragma omp for schedule(static)
            for (int i = 0; i < (int)od.size(); i++)
            {
                od.getLastQuaternion(i) = od.getOldQuaternion(i);
                od.getOldQuaternion(i) = od.getQuaternion(i);
                TimeIntegration::semiImplicitEulerRotation(h, od.getMass(i), od.getMass(i) * Matrix3r::Identity(), od.getInvMass(i) * Matrix3r::Identity(),od.getQuaternion(i), od.getVelocity(i), Vector3r(0,0,0));
            }
        }

        START_TIMING("position constraints projection");
        positionConstraintProjection(model);
        STOP_TIMING_AVG;

        #pragma omp parallel if(numBodies > MIN_PARALLEL_SIZE) default(shared)
        {
            // Update velocities
            #pragma omp for schedule(static) nowait
            for (int i = 0; i < numBodies; i++)
            {
                if (m_velocityUpdateMethod == 0)
                {
                    TimeIntegration::velocityUpdateFirstOrder(h, rb[i]->getMass(), rb[i]->getPosition(), rb[i]->getOldPosition(), rb[i]->getVelocity());
                    TimeIntegration::angularVelocityUpdateFirstOrder(h, rb[i]->getMass(), rb[i]->getRotation(), rb[i]->getOldRotation(), rb[i]->getAngularVelocity());
                }
                else
                {
                    TimeIntegration::velocityUpdateSecondOrder(h, rb[i]->getMass(), rb[i]->getPosition(), rb[i]->getOldPosition(), rb[i]->getLastPosition(), rb[i]->getVelocity());
                    TimeIntegration::angularVelocityUpdateSecondOrder(h, rb[i]->getMass(), rb[i]->getRotation(), rb[i]->getOldRotation(), rb[i]->getLastRotation(), rb[i]->getAngularVelocity());
                }
            }

            // Update velocities
            #pragma omp for schedule(static)
            for (int i = 0; i < (int) pd.size(); i++)
            {
                if (m_velocityUpdateMethod == 0)
                    TimeIntegration::velocityUpdateFirstOrder(h, pd.getMass(i), pd.getPosition(i), pd.getOldPosition(i), pd.getVelocity(i));
                else
                    TimeIntegration::velocityUpdateSecondOrder(h, pd.getMass(i), pd.getPosition(i), pd.getOldPosition(i), pd.getLastPosition(i), pd.getVelocity(i));
            }

            // Update velocites of orientations
            #pragma omp for schedule(static)
            for (int i = 0; i < (int)od.size(); i++)
            {
                if (m_velocityUpdateMethod == 0)
                    TimeIntegration::angularVelocityUpdateFirstOrder(h, od.getMass(i), od.getQuaternion(i), od.getOldQuaternion(i), od.getVelocity(i));
                else
                    TimeIntegration::angularVelocityUpdateSecondOrder(h, od.getMass(i), od.getQuaternion(i), od.getOldQuaternion(i), od.getLastQuaternion(i), od.getVelocity(i));
            }
        }
    }
    h = hOld;
    tm->setTimeStepSize(hOld);

    #pragma omp parallel default(shared)
    {
        #pragma omp for schedule(static) nowait
        for (int i = 0; i < numBodies; i++)
        {
            if (rb[i]->getMass() != 0.0)
                rb[i]->getGeometry().updateMeshTransformation(rb[i]->getPosition(), rb[i]->getRotationMatrix());
        }
    }


    if (m_collisionDetection)
    {
        START_TIMING("collision detection");
        m_collisionDetection->collisionDetection(model);
        STOP_TIMING_AVG;
    }

    velocityConstraintProjection(model);

    // update motor joint targets
    SimulationModel::ConstraintVector &constraints = model.getConstraints();
    for (unsigned int i = 0; i < constraints.size(); i++)
    {
        if ((constraints[i]->getTypeId() == TargetAngleMotorHingeJoint::TYPE_ID) ||
            (constraints[i]->getTypeId() == TargetVelocityMotorHingeJoint::TYPE_ID) ||
            (constraints[i]->getTypeId() == TargetPositionMotorSliderJoint::TYPE_ID) ||
            (constraints[i]->getTypeId() == TargetVelocityMotorSliderJoint::TYPE_ID))
        {
            MotorJoint *motor = (MotorJoint*)constraints[i];
            const std::vector<Real> sequence = motor->getTargetSequence();
            if (sequence.size() > 0)
            {
                Real time = tm->getTime();
                const Real sequenceDuration = sequence[sequence.size() - 2] - sequence[0];
                if (motor->getRepeatSequence())
                {
                    while (time > sequenceDuration)
                        time -= sequenceDuration;
                }
                unsigned int index = 0;
                while ((2*index < sequence.size()) && (sequence[2 * index] <= time))
                    index++;

                // linear interpolation
                Real target = 0.0;
                if (2 * index < sequence.size())
                {
                    const Real alpha = (time - sequence[2 * (index - 1)]) / (sequence[2 * index] - sequence[2 * (index - 1)]);
                    target = (static_cast<Real>(1.0) - alpha) * sequence[2 * index - 1] + alpha * sequence[2 * index + 1];
                }
                else
                    target = sequence[sequence.size() - 1];
                motor->setTarget(target);
            }
        }
    }

    // compute new time
    tm->setTime (tm->getTime () + h);
    STOP_TIMING_AVG;
}

void TimeStepController::reset()
{
    m_iterations = 0;
    m_iterationsV = 0;
    //m_maxIterations = 5;
    //m_maxIterationsV = 5;
}

void TimeStepController::positionConstraintProjection(SimulationModel &model)
{
    m_iterations = 0;

    // init constraint groups if necessary
    model.initConstraintGroups();

    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    SimulationModel::ConstraintVector &constraints = model.getConstraints();
    SimulationModel::ConstraintGroupVector &groups = model.getConstraintGroups();
    SimulationModel::RigidBodyContactConstraintVector &contacts = model.getRigidBodyContactConstraints();
    SimulationModel::ParticleSolidContactConstraintVector &particleTetContacts = model.getParticleSolidContactConstraints();

    // init constraints for this time step if necessary
    for (auto & constraint : constraints)
    {
        constraint->initConstraintBeforeProjection(model);
    }

    while (m_iterations < m_maxIterations)
    {
        for (unsigned int group = 0; group < groups.size(); group++)
        {
            const int groupSize = (int)groups[group].size();
            #pragma omp parallel if(groupSize > MIN_PARALLEL_SIZE) default(shared)
            {
                #pragma omp for schedule(static)
                for (int i = 0; i < groupSize; i++)
                {
                    const unsigned int constraintIndex = groups[group][i];

                    constraints[constraintIndex]->updateConstraint(model);
                    constraints[constraintIndex]->solvePositionConstraint(model, m_iterations);
                }
            }
        }

        for (unsigned int i = 0; i < particleTetContacts.size(); i++)
        {
            particleTetContacts[i].solvePositionConstraint(model, m_iterations);
        }

        m_iterations++;
    }
}


void TimeStepController::velocityConstraintProjection(SimulationModel &model)
{
    m_iterationsV = 0;

    // init constraint groups if necessary
    model.initConstraintGroups();

    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    SimulationModel::ConstraintVector &constraints = model.getConstraints();
    SimulationModel::ConstraintGroupVector &groups = model.getConstraintGroups();
    SimulationModel::RigidBodyContactConstraintVector &rigidBodyContacts = model.getRigidBodyContactConstraints();
    SimulationModel::ParticleRigidBodyContactConstraintVector &particleRigidBodyContacts = model.getParticleRigidBodyContactConstraints();
    SimulationModel::ParticleSolidContactConstraintVector &particleTetContacts = model.getParticleSolidContactConstraints();

    for (unsigned int group = 0; group < groups.size(); group++)
    {
        const int groupSize = (int)groups[group].size();
        #pragma omp parallel if(groupSize > MIN_PARALLEL_SIZE) default(shared)
        {
            #pragma omp for schedule(static)
            for (int i = 0; i < groupSize; i++)
            {
                const unsigned int constraintIndex = groups[group][i];
                constraints[constraintIndex]->updateConstraint(model);
            }
        }
    }

    while (m_iterationsV < m_maxIterationsV)
    {
        for (unsigned int group = 0; group < groups.size(); group++)
        {
            const int groupSize = (int)groups[group].size();
            #pragma omp parallel if(groupSize > MIN_PARALLEL_SIZE) default(shared)
            {
                #pragma omp for schedule(static)
                for (int i = 0; i < groupSize; i++)
                {
                    const unsigned int constraintIndex = groups[group][i];
                    constraints[constraintIndex]->solveVelocityConstraint(model, m_iterationsV);
                }
            }
        }

        // solve contacts
        for (unsigned int i = 0; i < rigidBodyContacts.size(); i++)
        {
            rigidBodyContacts[i].solveVelocityConstraint(model, m_iterationsV);
        }
        for (unsigned int i = 0; i < particleRigidBodyContacts.size(); i++)
        {
            particleRigidBodyContacts[i].solveVelocityConstraint(model, m_iterationsV);
        }
        for (unsigned int i = 0; i < particleTetContacts.size(); i++)
        {
            particleTetContacts[i].solveVelocityConstraint(model, m_iterationsV);
        }
        m_iterationsV++;
    }
}