/*  This file is part of the OpenLB library
 *
 *  Copyright (C) 2016 Thomas Henn
 *  E-mail contact: info@openlb.net
 *  The most recent release of OpenLB can be downloaded at
 *  
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public
 *  License along with this program; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA  02110-1301, USA.
 */
#ifndef PARTICLESYSTEM_3D_HH
#define PARTICLESYSTEM_3D_HH
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "particle3D.h"
#include "particleSystem3D.h"
#include "functors/analytical/frameChangeF3D.h"
//#include "../functors/frameChangeF3D.h" //check
//#include "../utilities/vectorHelpers.h" //check
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// For agglomeration functions
namespace olb {
template class PARTICLETYPE>
ParticleSystem3D::ParticleSystem3D( int iGeometry,
  SuperGeometry3D& superGeometry)
  : clout(std::cout, "ParticleSystem3D"),
    _iGeometry(iGeometry),
    _superGeometry(superGeometry),
    _contactDetection(new ContactDetection(*this)),
    _sim(this)
{
}
template class PARTICLETYPE>
ParticleSystem3D::ParticleSystem3D(
  const ParticleSystem3D& pS)
  : clout(std::cout, "ParticleSystem3D"),
    _iGeometry(pS._iGeometry),
    _superGeometry(pS._superGeometry),
    _contactDetection(new ContactDetection(*this)),
    _sim(this),
    _physPos(pS._physPos),
    _physExtend(pS._physExtend)
{
  _particles = pS._particles;
  _forces = pS._forces;
}
template class PARTICLETYPE>
ParticleSystem3D::ParticleSystem3D(
  ParticleSystem3D && pS)
  :     clout(std::cout, "ParticleSystem3D"),
        _iGeometry(pS._iGeometry),
        _superGeometry(pS._superGeometry),
        _contactDetection(new ContactDetection(*this)),
        _sim(this),
        _physPos(pS._physPos),
        _physExtend(pS._physExtend)
{
  _particles = std::move(pS._particles);
  _forces = std::move(pS._forces);
}
template class PARTICLETYPE>
ContactDetection* ParticleSystem3D::getContactDetection()
{
  return _contactDetection;
}
template class PARTICLETYPE>
void ParticleSystem3D::printDeep(std::string message)
{
  std::deque*> allParticles = this->getAllParticlesPointer();
  for (auto p : allParticles) {
    p->printDeep("");
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::setContactDetection(ContactDetection& contactDetection)
{
  delete _contactDetection;
  _contactDetection = contactDetection.generate(*this);
}
template class PARTICLETYPE>
void ParticleSystem3D::setPosExt(std::vector physPos,
    std::vector physExtend)
{
  _physPos = physPos;
  _physExtend = physExtend;
}
template class PARTICLETYPE>
const std::vector& ParticleSystem3D::getPhysPos()
{
  return _physPos;
}
template class PARTICLETYPE>
const std::vector& ParticleSystem3D::getPhysExtend()
{
  return _physExtend;
}
template class PARTICLETYPE>
PARTICLETYPE& ParticleSystem3D::operator[](const int i)
{
  if (i < (int) _particles.size()) {
    return _particles[i];
  } else if (i < (int) (_particles.size() + _shadowParticles.size())) {
    return _shadowParticles[i - _particles.size()];
  } else {
    std::ostringstream os;
    os << i;
    std::string err = "Cannot access element: " + os.str()
                      + " to large";
    throw std::runtime_error(err);
  }
}
template class PARTICLETYPE>
const PARTICLETYPE& ParticleSystem3D::operator[](
  const int i) const
{
  if ((unsigned)i < _particles.size()) {
    return _particles[i];
  } else if (i - _particles.size() < _shadowParticles.size()) {
    return _shadowParticles[i - _particles.size()];
  } else {
    std::ostringstream os;
    os << i;
    std::string err = "Cannot access element: " + os.str()
                      + " to large";
    throw std::runtime_error(err);
  }
}
template class PARTICLETYPE>
int ParticleSystem3D::size()
{
  return _particles.size();
}
template class PARTICLETYPE>
int ParticleSystem3D::sizeInclShadow() const
{
  return _particles.size() + _shadowParticles.size();
}
template class PARTICLETYPE>
int ParticleSystem3D::numOfActiveParticles()
{
  int activeParticles = 0;
  for (auto p : _particles) {
    if (p.getActive()) {
      activeParticles++;
    }
  }
  return activeParticles;
}
/*
template class PARTICLETYPE>
std::map ParticleSystem3D::radiusDistribution()
{
  std::map radDistr;
  typename std::map::iterator it;
  T rad = 0;
  for (auto p : _particles) {
    if (!p.getAggl()) {
      rad = p.getRad();
      if (radDistr.count(rad) > 0) {
        it = radDistr.find(rad);
        it->second = it->second + 1;
      } else {
        radDistr.insert(std::pair(rad, 1));
      }
    }
  }
  return radDistr;
}
*/
template class PARTICLETYPE>
int ParticleSystem3D::numOfForces()
{
  return _forces.size();
}
template class PARTICLETYPE>
void ParticleSystem3D::addParticle(PARTICLETYPE &p)
{
  _particles.push_back(p);
}
template class PARTICLETYPE>
void ParticleSystem3D::clearParticles()
{
  _particles.clear();
}
template class PARTICLETYPE>
void ParticleSystem3D::addShadowParticle(PARTICLETYPE &p)
{
  _shadowParticles.push_back(p);
}
template class PARTICLETYPE>
void ParticleSystem3D::addForce(
  std::shared_ptr > pF)
{
  _forces.push_back(pF);
}
template class PARTICLETYPE>
void ParticleSystem3D::addBoundary(
  std::shared_ptr > b)
{
  _boundaries.push_back(b);
}
template class PARTICLETYPE>
template
void ParticleSystem3D::setVelToFluidVel(
  SuperLatticeInterpPhysVelocity3D & fVel)
{
  for (auto& p : _particles) {
    if (p.getActive()) {
      fVel(&p.getVel()[0], &p.getPos()[0], p.getCuboid());
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::setVelToAnalyticalVel(
  AnalyticalConst3D& aVel)
{
  for (auto& p : _particles) {
    if (p.getActive()) {
      std::vector vel(3, T());
      aVel(&p.getVel()[0], &p.getPos()[0]);
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::computeForce()
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      p->resetForce();
      for (auto f : _forces) {
        f->applyForce(p, pInt, *this);
      }
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::computeBoundary()
{
  typename std::deque >::iterator p;
  for (auto f : _boundaries) {
    for (p = _particles.begin(); p != _particles.end(); ++p) {
      if (p->getActive()) {
        f->applyBoundary(p, *this);
      }
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::simulate(T dT, bool scale)
{
  _sim.simulate(dT, scale);
}
template class PARTICLETYPE>
void ParticleSystem3D::simulateWithTwoWayCoupling_Mathias ( T dT,
                                    ForwardCouplingModel& forwardCoupling,
                                    BackCouplingModel& backCoupling,
                                    int material, int subSteps, bool scale )
{
  _sim.simulateWithTwoWayCoupling_Mathias(dT, forwardCoupling, backCoupling, material, subSteps, scale);
}
template class PARTICLETYPE>
void ParticleSystem3D::simulateWithTwoWayCoupling_Davide ( T dT,
                                    ForwardCouplingModel& forwardCoupling,
                                    BackCouplingModel& backCoupling,
                                    int material, int subSteps, bool scale )
{
  _sim.simulateWithTwoWayCoupling_Davide(dT, forwardCoupling, backCoupling, material, subSteps, scale);
}
// multiple collision models
template class MagneticParticle3D>
void ParticleSystem3D::simulate(T dT, std::set sActivity, bool scale)
{
  _sim.simulate(dT, sActivity, scale);
}
template class PARTICLETYPE>
int ParticleSystem3D::countMaterial(int mat)
{
  int num = 0;
  int locLatCoords[3] = {0};
  for (auto& p : _particles) {
    _superGeometry.getCuboidGeometry().get(p.getCuboid()).getFloorLatticeR(
      locLatCoords, &p.getPos()[0]);
    const BlockGeometryStructure3D& bg = _superGeometry.getExtendedBlockGeometry(_superGeometry.getLoadBalancer().loc(p.getCuboid()));
    int iX = locLatCoords[0] + _superGeometry.getOverlap();
    int iY = locLatCoords[1] + _superGeometry.getOverlap();
    int iZ = locLatCoords[2] + _superGeometry.getOverlap();
    if    (bg.get(iX,     iY,     iZ) == mat
           || bg.get(iX,     iY + 1, iZ) == mat
           || bg.get(iX,     iY,     iZ + 1) == mat
           || bg.get(iX,     iY + 1, iZ + 1) == mat
           || bg.get(iX + 1, iY,     iZ) == mat
           || bg.get(iX + 1, iY + 1, iZ) == mat
           || bg.get(iX + 1, iY,     iZ + 1) == mat
           || bg.get(iX + 1, iY + 1, locLatCoords[2] + 1) == mat) {
      num++;
    }
  }
  return num;
}
template class PARTICLETYPE>
bool ParticleSystem3D::executeForwardCoupling(ForwardCouplingModel& forwardCoupling)
{
  for (auto& p : _particles) {
    if (p.getActive()) {
      if (! forwardCoupling(&p, _iGeometry) ) {
        std::cout << "ERROR: ForwardCoupling functional failed on particle " << p.getID();
        return false;
      }
    }
  }
  return true;
}
template class PARTICLETYPE>
bool ParticleSystem3D::executeBackwardCoupling(BackCouplingModel& backCoupling, int material, int subSteps)
{
  for (auto& p : _particles) {
    if (p.getActive()) {
      if (! backCoupling(&p, _iGeometry, material) ) {
        std::cout << "ERROR: BackwardCoupling functional failed on particle " << p.getID();
        return false;
      }
    }
  }
  return true;
}
template class PARTICLETYPE>
void ParticleSystem3D::explicitEuler(T dT, bool scale)
{
  T maxDeltaR = _superGeometry.getCuboidGeometry().getMaxDeltaR();
  T maxFactor = T();
  for (auto& p : _particles) {
    if (p.getActive()) {
      for (int i = 0; i < 3; i++) {
        p.getVel()[i] += p.getForce()[i] * p.getInvMass() * dT;
        p.getPos()[i] += p.getVel()[i] * dT;
        // computation of direction depending maxFactor to scale velocity value
        // to keep velocity small enough for simulation
        if (fabs(p.getVel()[i]) > fabs(maxDeltaR / dT)) {
          maxFactor = std::max(maxFactor, fabs(p.getVel()[i] / maxDeltaR * dT));
        }
      }
      // scaling of velocity values
      // if particles are too fast, e.g. material boundary can not work anymore
      if ( !util::nearZero(maxFactor) && scale) {
        std::cout << "particle velocity is scaled because of reached limit"
                  << std::endl;
        for (int i = 0; i < 3; i++) {
          p.getPos()[i] -= p.getVel()[i] * dT; // position set back
          p.getVel()[i] /= maxFactor; // scale velocity value
          p.getPos()[i] += p.getVel()[i] * dT;
        }
      }
      // if particles are too fast, e.g. material boundary can not work anymore
//#ifdef OLB_DEBUG
//      if (p.getVel()[i] * dT > _superGeometry.getCuboidGeometry().getMaxDeltaR()) {
//        std::cout << " PROBLEM: particle speed too high rel. to delta of "
//                  "lattice: "<< std::endl;
//        std::cout << "p.getVel()[i]*dT: " << i <<" "<< p.getVel()[i] * dT;
//        std::cout << "MaxDeltaR(): " <<
//                  _superGeometry.getCuboidGeometry().getMaxDeltaR() << std::endl;
//        exit(-1);
//      }
//#endif
    }
  }
}
template<>
void ParticleSystem3D::explicitEuler(double dT, bool scale)
{
  double maxDeltaR = _superGeometry.getCuboidGeometry().getMaxDeltaR();
  double maxFactor = double();
  for (auto& p : _particles) {
    if (p.getActive()) {
      if (p.getSActivity() == 3) {continue;}
      for (int i = 0; i < 3; i++) {
        p.getVel()[i] += p.getForce()[i] * p.getInvMass() * dT;
        p.getPos()[i] += p.getVel()[i] * dT;
        // computation of direction depending maxFactor to scale velocity value
        // to keep velocity small enough for simulation
        if (fabs(p.getVel()[i]) > fabs(maxDeltaR / dT)) {
          maxFactor = std::max(maxFactor, fabs(p.getVel()[i] / maxDeltaR * dT));
        }
      }
      // scaling of velocity values
      // if particles are too fast, e.g. material boundary can not work anymore
      if ( !util::nearZero(maxFactor) && scale) {
        std::cout << "particle velocity is scaled because of reached limit"
                  << std::endl;
        for (int i = 0; i < 3; i++) {
          p.getPos()[i] -= p.getVel()[i] * dT; // position set back
          p.getVel()[i] /= maxFactor; // scale velocity value
          p.getPos()[i] += p.getVel()[i] * dT;
        }
      }
      // Change sActivity of particle in dependence of its position in the geometry
      // if ((p.getPos()[0] > 0.00015) && (p.getSActivity() == 0)) {
      //   p.setSActivity(2);
      // }
      // }
      // if particles are too fast, e.g. material boundary can not work anymore
//#ifdef OLB_DEBUG
//      if (p.getVel()[i] * dT > _superGeometry.getCuboidGeometry().getMaxDeltaR()) {
//        std::cout << " PROBLEM: particle speed too high rel. to delta of "
//                  "lattice: "<< std::endl;
//        std::cout << "p.getVel()[i]*dT: " << i <<" "<< p.getVel()[i] * dT;
//        std::cout << "MaxDeltaR(): " <<
//                  _superGeometry.getCuboidGeometry().getMaxDeltaR() << std::endl;
//        exit(-1);
//      }
//#endif
    }
  }
}
// multiple collision models
template<>
void ParticleSystem3D::explicitEuler(double dT, std::set sActivityOfParticle, bool scale)
{
  double maxDeltaR = _superGeometry.getCuboidGeometry().getMaxDeltaR();
  double maxFactor = double();
  for (auto& p : _particles) {
    if (p.getActive()) {
      if (p.getSActivity() == 3) {continue;}
      bool b = false;
      for (auto sA : sActivityOfParticle) {
        if (p.getSActivity() == sA) {b = true; break;}
      }
      if (b == false) {continue;}
      for (int i = 0; i < 3; i++) {
        p.getVel()[i] += p.getForce()[i] * p.getInvMass() * dT;
        p.getPos()[i] += p.getVel()[i] * dT;
        // computation of direction depending maxFactor to scale velocity value
        // to keep velocity small enough for simulation
        if (fabs(p.getVel()[i]) > fabs(maxDeltaR / dT)) {
          maxFactor = std::max(maxFactor, fabs(p.getVel()[i] / maxDeltaR * dT));
        }
      }
      // scaling of velocity values
      // if particles are too fast, e.g. material boundary can not work anymore
      if ( !util::nearZero(maxFactor) && scale) {
        std::cout << "particle velocity is scaled because of reached limit"
                  << std::endl;
        for (int i = 0; i < 3; i++) {
          p.getPos()[i] -= p.getVel()[i] * dT; // position set back
          p.getVel()[i] /= maxFactor; // scale velocity value
          p.getPos()[i] += p.getVel()[i] * dT;
        }
      }
      // Change sActivity of particle in dependence of its position in the geometry
      // if ((p.getPos()[0] > 0.00015) && (p.getSActivity() == 0)) {
      //   p.setSActivity(2);
      // }
      // }
      // if particles are too fast, e.g. material boundary can not work anymore
//#ifdef OLB_DEBUG
//      if (p.getVel()[i] * dT > _superGeometry.getCuboidGeometry().getMaxDeltaR()) {
//        std::cout << " PROBLEM: particle speed too high rel. to delta of "
//                  "lattice: "<< std::endl;
//        std::cout << "p.getVel()[i]*dT: " << i <<" "<< p.getVel()[i] * dT;
//        std::cout << "MaxDeltaR(): " <<
//                  _superGeometry.getCuboidGeometry().getMaxDeltaR() << std::endl;
//        exit(-1);
//      }
//#endif
    }
  }
}
//template class PARTICLETYPE>
//void ParticleSystem3D::integrateTorque(T dT)
//{
//  for (auto& p : _particles) {
//    if (p.getActive()) {
//      for (int i = 0; i < 3; i++) {
//        p.getAVel()[i] += p.getTorque()[i] * 1.
//                          / (2. / 5. * p.getMass() * std::pow(p.getRad(), 2)) * dT;
//      }
//    }
//  }
//}
//template class PARTICLETYPE>
//void ParticleSystem3D::integrateTorqueMag(T dT) {
////template
////void ParticleSystem3D::integrateTorqueMag(T dT) {
//  for (auto& p : _particles) {
//    if (p.getActive()) {
//      Vector deltaAngle;
//      T angle;
//      T epsilon = std::numeric_limits::epsilon();
//      for (int i = 0; i < 3; i++) {
//        // change orientation due to torque moments
//        deltaAngle[i] = (5. * p.getTorque()[i] * dT * dT) / (2. * p.getMass() * std::pow(p.getRad(), 2));
//        // apply change in angle to dMoment vector
//      }
//      angle = norm(deltaAngle);
//      if (angle > epsilon) {
//        //Vector axis(deltaAngle);
//        //  Vector axis(T(), T(), T(1));
//        //axis.normalize();
//        std::vector null(3, T());
//
//        //RotationRoundAxis3D rotRAxis(p.getPos(), fromVector3(axis), angle);
//        RotationRoundAxis3D rotRAxis(null, fromVector3(deltaAngle), angle);
//        T input[3] = {p.getMoment()[0], p.getMoment()[1], p.getMoment()[2]};
//        Vector in(input);
//        T output[3] = {T(), T(), T()};
//        rotRAxis(output, input);
//        Vector out(output);
////        std::vector mainRefVec(3, T());
////        mainRefVec[0] = 1.;
////        std::vector secondaryRefVec(3, T());
////        secondaryRefVec[2] = 1.;
////        AngleBetweenVectors3D checkAngle(mainRefVec, secondaryRefVec);
////        T angle[1];
////        checkAngle(angle, input);
//        std::cout<< "|moment|_in: " << in.norm() << ", |moment|_out: " << out.norm()
//                 << ", | |in| - |out| |: " << fabs(in.norm() - out.norm())
//                 /*<< " Angle: " << angle[0] */<< std::endl;
//        p.getMoment()[0] = output[0];
//        p.getMoment()[1] = output[1];
//        p.getMoment()[2] = output[2];
//      }
//    }
//  }
//}
//template class PARTICLETYPE>
//void ParticleSystem3D::implicitEuler(T dT, AnalyticalF3D& getvel) {
//  _activeParticles = 0;
//  for (auto& p : _particles) {
//    if(p.getActive()) {
//      std::vector fVel = getvel(p._pos);
////      std::vector vel = p.getVel();
//      std::vector pos = p.getPos();
//      T C = 6.* M_PI * p._rad * this->_converter.getCharNu() * this->_converter.getCharRho()* dT/p.getMass();
//      for (int i = 0; i<3; i++) {
//        p._vel[i] = (p._vel[i]+C*fVel[i]) / (1+C);
//        p._pos[i] += p._vel[i] * dT;
//      }
////      p.setVel(vel);
////      p.setPos(pos);
//      checkActive(p);
////      cout << "C: " << C << std::endl;
//    }
////    cout << "pos: " << p._pos[0] << " " << p._pos[1] << " " << p._pos[2] << " " << p._vel[0] << " " << p._vel[1] << " " << p._vel[2]<< std::endl; //"\n force: " << p._force[0] << " " << p._force[1] << " " << p._force[2]<< "\n " << std::endl;
//  }
//}
/*
template class PARTICLETYPE>
void ParticleSystem3D::predictorCorrector1(T dT)
{
  std::vector vel;
  std::vector pos;
  std::vector frc;
  for (auto& p : _particles) {
    if (p.getActive()) {
      vel = p.getVel();
      p.setVel(vel, 1);
      pos = p.getPos();
      p.setVel(pos, 2);
      frc = p.getForce();
      p.setForce(frc, 1);
      for (int i = 0; i < 3; i++) {
        vel[i] += p._force[i] / p.getMass() * dT;
        pos[i] += vel[i] * dT;
      }
      p.setVel(vel);
      p.setPos(pos);
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::predictorCorrector2(T dT)
{
  std::vector vel;
  std::vector pos;
  std::vector frc;
  for (auto& p : _particles) {
    if (p.getActive()) {
      vel = p.getVel(1);
      pos = p.getVel(2);
      for (int i = 0; i < 3; i++) {
        vel[i] += dT * .5 * (p.getForce()[i] + p.getForce(1)[i]) / p.getMass();
        pos[i] += vel[i] * dT;
      }
      p.setVel(vel);
      p.setPos(pos);
    }
  }
}
*/
/*
template class PARTICLETYPE>
void ParticleSystem3D::adamBashforth4(T dT)
{
  for (auto& p : _particles) {
    if (p.getActive()) {
      std::vector vel = p.getVel();
      std::vector pos = p.getPos();
      for (int i = 0; i < 3; i++) {
        vel[i] += dT / p.getMas()
                  * (55. / 24. * p.getForce()[i] - 59. / 24. * p.getForce(1)[i]
                     + 37. / 24. * p.getForce(2)[i] - 9. / 24. * p.getForce(3)[i]);
      }
      p.rotAndSetVel(vel);
      for (int i = 0; i < 3; i++) {
        pos[i] += dT
                  * (55. / 24. * p.getVel()[i] - 59. / 24. * p.getVel(1)[i]
                     + 37. / 24. * p.getVel(2)[i] - 3. / 8. * p.getVel(3)[i]);
      }
      p.setPos(pos);
    }
  }
}
*/
template class PARTICLETYPE>
void ParticleSystem3D::velocityVerlet1(T dT)
{
  for (auto& p : _particles) {
    if (p.getActive()) {
      std::vector pos = p.getPos();
      for (int i = 0; i < 3; i++) {
        pos[i] += p.getVel()[i] * dT
                  + .5 * p.getForce()[i] / p.getMass() * std::pow(dT, 2);
      }
      p.setPos(pos);
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::velocityVerlet2(T dT)
{
  std::vector frc;
  std::vector vel;
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
//  for (auto& p : _particles) {
    if (p->getActive()) {
      frc = p->getForce();
      vel = p->getVel();
      p->resetForce();
      for (auto f : _forces) {
        f->applyForce(p, pInt, *this);
      }
      for (int i = 0; i < 3; i++) {
        vel[i] += .5 * (p->getForce()[i] + frc[i]) / p->getMass() * dT;
      }
      p->setVel(vel);
    }
  }
}
/*
template class PARTICLETYPE>
void ParticleSystem3D::rungeKutta4(T dT)
{
  rungeKutta4_1(dT);
  rungeKutta4_2(dT);
  rungeKutta4_3(dT);
  rungeKutta4_4(dT);
}
template class PARTICLETYPE>
void ParticleSystem3D::rungeKutta4_1(T dT)
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      p->resetForce();
      for (auto f : _forces) {
        f->applyForce(p, pInt, *this);
      }
      std::vector k1 = p->getForce();
      p->setForce(k1, 3);
      std::vector storeVel = p->getVel();
      p->setVel(storeVel, 3);
      std::vector storePos = p->getPos();
      p->setVel(storePos, 2);
      std::vector vel(3, T()), pos(3, T());
      for (int i = 0; i < 3; i++) {
        vel[i] = p->getVel(3)[i] + dT / 2. / p->getMass() * k1[i];
        pos[i] = p->getVel(2)[i] + dT / 2. * vel[i];
      }
      p->setVel(vel);
      p->setPos(pos);
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::rungeKutta4_2(T dT)
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      p->resetForce();
      for (auto f : _forces) {
        f->applyForce(p, pInt, *this);
      }
      std::vector k2 = p->getForce();
      p->setForce(k2, 2);
      std::vector vel(3, T()), pos(3, T());
      for (int i = 0; i < 3; i++) {
        vel[i] = p->getVel(3)[i] + dT / 2. / p->getMass() * k2[i];
        pos[i] = p->getVel(2)[i] + dT / 2. * vel[i];
      }
      p->setVel(vel);
      p->setPos(pos);
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::rungeKutta4_3(T dT)
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      p->resetForce();
      for (auto f : _forces) {
        f->applyForce(p, pInt, *this);
      }
      std::vector k3 = p->getForce();
      p->setForce(k3, 1);
      std::vector vel(3, T()), pos(3, T());
      for (int i = 0; i < 3; i++) {
        vel[i] = p->getVel(3)[i] + dT / p->getMass() * k3[i];
        pos[i] = p->getVel(2)[i] + dT * vel[i];
      }
      p->setVel(vel);
      p->setPos(pos);
    }
  }
}
template class PARTICLETYPE>
void ParticleSystem3D::rungeKutta4_4(T dT)
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      p->resetForce();
      for (auto f : _forces) {
        f->applyForce(p, pInt, *this);
      }
      std::vector k4 = p->getForce();
      std::vector vel(3, T()), pos(3, T());
      for (int i = 0; i < 3; i++) {
        vel[i] = p->getVel(3)[i]
                 + dT / 6. / p->getMass()
                 * (p->getForce(3)[i] + 2 * p->getForce(2)[i]
                    + 2 * p->getForce(1)[i] + p->getForce()[i]);
        pos[i] = p->getVel(2)[i] + dT * vel[i];
      }
      p->setVel(vel);
      p->setPos(pos);
    }
  }
}
*/
template class PARTICLETYPE>
std::deque*> ParticleSystem3D::
getParticlesPointer()
{
  std::deque*> pointerParticles;
  for (int i = 0; i < (int) _particles.size(); i++) {
    pointerParticles.push_back(&_particles[i]);
  }
  return pointerParticles;
}
template class PARTICLETYPE>
std::list > >
ParticleSystem3D::getForcesPointer()
{
  return _forces;
}
template class PARTICLETYPE>
std::deque*> ParticleSystem3D::
getAllParticlesPointer()
{
  std::deque*> pointerParticles;
  int i = 0;
  for (; i < (int) _particles.size(); i++) {
    pointerParticles.push_back(&_particles[i]);
  }
  for (; i < (int) (_particles.size() + _shadowParticles.size()); i++) {
    pointerParticles.push_back(&_shadowParticles[i - _particles.size()]);
  }
  return pointerParticles;
}
template class PARTICLETYPE>
std::deque*> ParticleSystem3D::
getShadowParticlesPointer() {
  std::deque*> pointerParticles;
  for (int i = 0; i < (int) (_shadowParticles.size()); i++) {
    pointerParticles.push_back(&_shadowParticles[i]);
  }
  return pointerParticles;
}
template class PARTICLETYPE>
void ParticleSystem3D::saveToFile(std::string fullName)
{
  std::ofstream fout(fullName.c_str(), std::ios::app);
  if (!fout) {
    clout << "Error: could not open " << fullName << std::endl;
  }
  std::vector serPar(PARTICLETYPE::serialPartSize, 0);
  for (auto& p : _particles) {
    p.serialize(&serPar[0]);
    typename std::vector::iterator it = serPar.begin();
    for (; it != serPar.end(); ++it) {
      fout << *it << " ";
    }
    fout << std::endl;
    //fout << p.getPos()[0] << " " << p.getPos()[1] << " " << p.getPos()[2] << " " << p.getVel()[0] << " " << p.getVel()[1] << " "<< p.getVel()[2] << std::endl;
  }
  fout.close();
}
//template class PARTICLETYPE>
//template class DESCRIPTOR>
//void ParticleSystem3D::particleOnFluid(
//  BlockLatticeStructure3D& bLattice, Cuboid3D& cuboid,
//  int overlap, T eps, BlockGeometryStructure3D& bGeometry)
//{
//  T rad = 0;
//  std::vector vel(3, T());
//  T minT[3] = {0}, maxT[3] = {0}, physR[3] = {0};
//  int min[3] = {0}, max[3] = {0};
//  //cout << "OVERLAP: " << overlap << std::endl;
//  for (int i = 0; i < sizeInclShadow(); ++i) {
//    rad = operator[](i).getRad();
//    minT[0] = operator[](i).getPos()[0] - rad * eps;
//    minT[1] = operator[](i).getPos()[1] - rad * eps;
//    minT[2] = operator[](i).getPos()[2] - rad * eps;
//    maxT[0] = operator[](i).getPos()[0] + rad * eps;
//    maxT[1] = operator[](i).getPos()[1] + rad * eps;
//    maxT[2] = operator[](i).getPos()[2] + rad * eps;
//    cuboid.getLatticeR(min, minT);
//    cuboid.getLatticeR(max, maxT);
//    max[0]++;
//    max[1]++;
//    max[2]++;
//    T porosity = 0;
//    T dist = 0;
//    for (int iX = min[0]; iX < max[0]; ++iX) {
//      for (int iY = min[1]; iY < max[1]; ++iY) {
//        for (int iZ = min[2]; iZ < max[2]; ++iZ) {
//          cuboid.getPhysR(physR, iX, iY, iZ);
//          dist = std::pow(physR[0] - operator[](i).getPos()[0], 2)
//                 + std::pow(physR[1] - operator[](i).getPos()[1], 2)
//                 + std::pow(physR[2] - operator[](i).getPos()[2], 2);
//          if (dist < rad * rad * eps * eps) {
//            vel = operator[](i).getVel();
//            vel[0] = _converter.latticeVelocity(vel[0]);
//            vel[1] = _converter.latticeVelocity(vel[1]);
//            vel[2] = _converter.latticeVelocity(vel[2]);
//            if (dist < rad * rad) {
//              porosity = 0;
//              bLattice.get(iX, iY, iZ).defineField(
//                &porosity);
//              bLattice.get(iX, iY, iZ).defineField(
//                &vel[0]);
//            } else {
//              T d = std::sqrt(dist) - rad;
//              porosity = 1.
//                         - std::pow(std::cos(M_PI * d / (2. * (eps - 1.) * rad)), 2);
//              bLattice.get(iX, iY, iZ).defineField(
//                &porosity);
//              bLattice.get(iX, iY, iZ).defineField(
//                &vel[0]);
//            }
//          }
//        }
//      }
//    }
//  }
//}
//
//template class PARTICLETYPE>
//template class DESCRIPTOR>
//void ParticleSystem3D::resetFluid(
//  BlockLatticeStructure3D& bLattice, Cuboid3D& cuboid,
//  int overlap)
//{
//  T rad = 0, vel[3] = {0};
//  T minT[3] = {0}, maxT[3] = {0}, physR[3] = {0};
//  int min[3] = {0}, max[3] = {0};
//  for (int i = 0; i < sizeInclShadow(); ++i) {
//    rad = operator[](i).getRad();
//    minT[0] = operator[](i).getPos()[0] - rad * 1.2;
//    minT[1] = operator[](i).getPos()[1] - rad * 1.2;
//    minT[2] = operator[](i).getPos()[2] - rad * 1.2;
//    maxT[0] = operator[](i).getPos()[0] + rad * 1.2;
//    maxT[1] = operator[](i).getPos()[1] + rad * 1.2;
//    maxT[2] = operator[](i).getPos()[2] + rad * 1.2;
//    cuboid.getLatticeR(min, minT);
//    cuboid.getLatticeR(max, maxT);
//    max[0]++;
//    max[1]++;
//    max[2]++;
//    T porosity = 1;
//    T dist = 0;
//    for (int iX = min[0]; iX < max[0]; ++iX) {
//      for (int iY = min[1]; iY < max[1]; ++iY) {
//        for (int iZ = min[2]; iZ < max[2]; ++iZ) {
//          bLattice.get(iX, iY, iZ).defineField(
//            &porosity);
//          bLattice.get(iX, iY, iZ).defineField(
//            vel);
//          //          }
//        }
//      }
//    }
//  }
//}
template<>
void ParticleSystem3D::resetMag()
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      p->resetForce();
      p->resetTorque();
    }
  }
}
// multiple collision models
template<>
void ParticleSystem3D::resetMag(std::set sActivityOfParticle)
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getSActivity() == 3) {continue;}
    if (p->getActive()) {
      bool b = false;
      for (auto sA : sActivityOfParticle) {
        if (p->getSActivity() == sA) {b = true; break;}
      }
      if (b == false) {continue;}
      p->resetForce();
      p->resetTorque();
    }
  }
}
template<>
void ParticleSystem3D::computeForce()
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      for (auto f : _forces) {
        f->applyForce(p, pInt, *this);
      }
    }
  }
}
// multiple collision models
template<>
void ParticleSystem3D::computeForce(std::set sActivityOfParticle)
{
  typename std::deque >::iterator p;
  int pInt = 0;
  for (p = _particles.begin(); p != _particles.end(); ++p, ++pInt) {
    if (p->getActive()) {
      if (p->getSActivity() == 3) {continue;}
      bool b = false;
      for (auto sA : sActivityOfParticle) {
        if (p->getSActivity() == sA) {b = true; break;}
      }
      if (b == false) {continue;}
      for (auto f : _forces) {
        // f->applyForce(p, p->getID(), *this);
        f->applyForce(p, pInt, *this);
      }
    }
  }
}
/* Original MagDM damping intern
template<>
void ParticleSystem3D::integrateTorqueMag(double dT)
{
  for (auto& p : _particles) {
    Vector deltaAngle;
    double angle;
    double epsilon = std::numeric_limits::epsilon();
    double damping = std::pow((1. - p.getADamping()), dT);
    for (int i = 0; i < 3; i++) {
      p.getAVel()[i] += (5. * (p.getTorque()[i]) * dT) / (2.  * p.getMass() * std::pow(p.getRad(), 2));
      p.getAVel()[i] *= damping;
      deltaAngle[i] = p.getAVel()[i] * dT;
    angle = norm(deltaAngle);
    if (angle > epsilon) {
      std::vector null(3, double());
      RotationRoundAxis3D rotRAxis(null, util::fromVector3(deltaAngle), angle);
      double input[3] = {p.getMoment()[0], p.getMoment()[1], p.getMoment()[2]};
      Vector in(input);
      double output[3] = {double(), double(), double()};
      rotRAxis(output, input);
      Vector out(output);
      // renormalize output
      if (out.norm() > epsilon) {
        out = (1. / out.norm()) * out;
      }
      p.getMoment()[0] = out[0];
      p.getMoment()[1] = out[1];
      p.getMoment()[2] = out[2];
    }
  }
}
}
*/
template<>
void ParticleSystem3D::integrateTorqueMag(double dT)
{
  for (auto& p : _particles) {
    Vector deltaAngle;
    double angle;
    double epsilon = std::numeric_limits::epsilon();
    for (int i = 0; i < 3; i++) {
      p.getAVel()[i] += (5. * (p.getTorque()[i]) * dT) / (2.  * p.getMass() * std::pow(p.getRad(), 2));
      deltaAngle[i] = p.getAVel()[i] * dT;
      angle = norm(deltaAngle);
      if (angle > epsilon) {
        std::vector null(3, double());
        RotationRoundAxis3D rotRAxis(null, util::fromVector3(deltaAngle), angle);
        double input[3] = {p.getMoment()[0], p.getMoment()[1], p.getMoment()[2]};
        Vector in(input);
        double output[3] = {double(), double(), double()};
        rotRAxis(output, input);
        Vector out(output);
        // renormalize output
        if (out.norm() > epsilon) {
          out = (1. / out.norm()) * out;
        }
        p.getMoment()[0] = out[0];
        p.getMoment()[1] = out[1];
        p.getMoment()[2] = out[2];
      }
    }
  }
}
// multiple collision models
template<>
void ParticleSystem3D::integrateTorqueMag(double dT, std::set sActivityOfParticle)
{
  for (auto& p : _particles) {
    if (p.getSActivity() == 3) {continue;}
    bool b = false;
    for (auto sA : sActivityOfParticle) {
      if (p.getSActivity() == sA) {b = true; break;}
    }
    if (b == false) {continue;}
    Vector deltaAngle;
    double angle;
    double epsilon = std::numeric_limits::epsilon();
    for (int i = 0; i < 3; i++) {
      p.getAVel()[i] += (5. * (p.getTorque()[i]) * dT) / (2.  * p.getMass() * std::pow(p.getRad(), 2));
      deltaAngle[i] = p.getAVel()[i] * dT;
      angle = norm(deltaAngle);
      if (angle > epsilon) {
        std::vector null(3, double());
        RotationRoundAxis3D rotRAxis(null, util::fromVector3(deltaAngle), angle);
        double input[3] = {p.getMoment()[0], p.getMoment()[1], p.getMoment()[2]};
        Vector in(input);
        double output[3] = {double(), double(), double()};
        rotRAxis(output, input);
        Vector out(output);
        // renormalize output
        if (out.norm() > epsilon) {
          out = (1. / out.norm()) * out;
        }
        p.getMoment()[0] = out[0];
        p.getMoment()[1] = out[1];
        p.getMoment()[2] = out[2];
      }
    }
  }
}
template