void Dynamics::outputParticleXMLData(magnet::xml::XmlStream& XML, bool applyBC) const { XML << magnet::xml::tag("ParticleData"); if (hasOrientationData()) XML << magnet::xml::attr("OrientationData") << "Y"; for (size_t i = 0; i < Sim->N; ++i) { Particle tmp(Sim->particles[i]); if (applyBC) Sim->BCs->applyBC(tmp.getPosition(), tmp.getVelocity()); tmp.getVelocity() *= (1.0 / Sim->units.unitVelocity()); tmp.getPosition() *= (1.0 / Sim->units.unitLength()); XML << magnet::xml::tag("Pt"); Sim->_properties.outputParticleXMLData(XML, i); XML << tmp; if (hasOrientationData()) XML << magnet::xml::tag("O") << orientationData[i].angularVelocity << magnet::xml::endtag("O") << magnet::xml::tag("U") << orientationData[i].orientation << magnet::xml::endtag("U") ; XML << magnet::xml::endtag("Pt"); } XML << magnet::xml::endtag("ParticleData"); }
void Dynamics::initialise() { streamFreq = 10 * Sim->N; if (hasOrientationData()) { double sumEnergy(0.0); for (const Particle& part : Sim->particles) sumEnergy += Sim->species[part]->getScalarMomentOfInertia(part.getID()) * orientationData[part.getID()].angularVelocity.nrm2(); //Check if any of the species are overridden bool hasInertia(false); for (const shared_ptr<Species>& spec : Sim->species) if (std::dynamic_pointer_cast<SpInertia>(spec)) hasInertia = true; if (!hasInertia) M_throw() << "No species have inertia, yet the particles have orientational degrees of freedom set!"; sumEnergy *= 0.5 / Sim->units.unitEnergy(); dout << "System Rotational Energy " << sumEnergy << "\nRotational kT " << sumEnergy / Sim->N << std::endl; } }
void Dynamics::rescaleSystemKineticEnergy(const double& scale) { double scalefactor(sqrt(scale)); if (std::dynamic_pointer_cast<BCLeesEdwards>(Sim->BCs)) { const BCLeesEdwards& bc = static_cast<const BCLeesEdwards&>(*Sim->BCs); for (Particle& part : Sim->particles) { const double mass = Sim->species[part]->getMass(part.getID()); if (!std::isinf(mass)) part.getVelocity() = Vector(bc.getPeculiarVelocity(part) * scalefactor + bc.getStreamVelocity(part)); } } else for (Particle& part : Sim->particles) { const double mass = Sim->species[part]->getMass(part.getID()); if (!std::isinf(mass)) part.getVelocity() *= scalefactor; } if (hasOrientationData()) for (Particle& part : Sim->particles) { const double I = Sim->species[part]->getScalarMomentOfInertia(part.getID()); if (!std::isinf(I)) orientationData[part.getID()].angularVelocity *= scalefactor; } }
ParticleEventData DynNewtonian::runAndersenWallCollision(Particle& part, const Vector & vNorm, const double& sqrtT, const double) const { updateParticle(part); if (hasOrientationData()) M_throw() << "Need to implement thermostating of the rotational degrees" " of freedom"; //This gives a completely new random unit vector with a properly //distributed Normal component. See Granular Simulation Book ParticleEventData tmpDat(part, *Sim->species[part], WALL); double mass = Sim->species[tmpDat.getSpeciesID()]->getMass(part.getID()); for (size_t iDim = 0; iDim < NDIM; iDim++) part.getVelocity()[iDim] = Sim->normal_sampler() * sqrtT / std::sqrt(mass); part.getVelocity() //This first line adds a component in the direction of the normal += vNorm * (sqrtT * sqrt(-2.0*log(1.0-Sim->uniform_sampler()) / mass) //This removes the original normal component -(part.getVelocity() | vNorm)); tmpDat.setDeltaKE(0.5 * mass * (part.getVelocity().nrm2() - tmpDat.getOldVel().nrm2())); return tmpDat; }
ParticleEventData DynNewtonian::randomGaussianEvent(Particle& part, const double& sqrtT, const size_t dimensions) const { #ifdef DYNAMO_DEBUG if (dimensions > NDIM) M_throw() << "Number of dimensions passed larger than NDIM!"; #endif //See http://mathworld.wolfram.com/SpherePointPicking.html if (hasOrientationData()) M_throw() << "Need to implement thermostating of the rotational degrees" " of freedom"; //Ensure the particle is free streamed first updateParticle(part); //Collect the precoll data ParticleEventData tmpDat(part, *Sim->species[part], GAUSSIAN); double mass = Sim->species[tmpDat.getSpeciesID()]->getMass(part.getID()); double factor = sqrtT / std::sqrt(mass); //Assign the new velocities for (size_t iDim = 0; iDim < dimensions; iDim++) part.getVelocity()[iDim] = Sim->normal_sampler() * factor; tmpDat.setDeltaKE(0.5 * mass * (part.getVelocity().nrm2() - tmpDat.getOldVel().nrm2())); return tmpDat; }
std::pair<bool, double> DynCompression::getOffcentreSpheresCollision(const double offset1, const double diameter1, const double offset2, const double diameter2, const Particle& p1, const Particle& p2, double t_max, double maxdist) const { #ifdef DYNAMO_DEBUG if (!hasOrientationData()) M_throw() << "Cannot use this function without orientational data"; if (!isUpToDate(p1)) M_throw() << "Particle1 " << p1.getID() << " is not up to date"; if (!isUpToDate(p2)) M_throw() << "Particle2 " << p2.getID() << " is not up to date"; #endif Vector r12 = p1.getPosition() - p2.getPosition(); Vector v12 = p1.getVelocity() - p2.getVelocity(); Sim->BCs->applyBC(r12, v12); const double limit_time_window = 1 / growthRate; std::pair<bool, double> retval = magnet::intersection::offcentre_growing_spheres(r12, v12, orientationData[p1.getID()].angularVelocity, orientationData[p2.getID()].angularVelocity, orientationData[p1.getID()].orientation * Quaternion::initialDirector() * offset1, orientationData[p2.getID()].orientation * Quaternion::initialDirector() * offset2, diameter1, diameter2, maxdist, std::min(limit_time_window, t_max), Sim->systemTime, growthRate); //Check if there's no collision reported but we've limited the interval if ((retval.second == HUGE_VAL) && (t_max > limit_time_window)) return std::pair<bool, double>(false, limit_time_window); //Otherwise return what was calculated return retval; }
ParticleEventData DynCompression::runAndersenWallCollision(Particle& part, const Vector & vNorm, const double& sqrtT, const double d) const { updateParticle(part); if (hasOrientationData()) M_throw() << "Need to implement thermostating of the rotational degrees" " of freedom"; //This gives a completely new random unit vector with a properly //distributed Normal component. See Granular Simulation Book ParticleEventData tmpDat(part, *Sim->species[part], WALL); double mass = Sim->species[tmpDat.getSpeciesID()]->getMass(part.getID()); std::normal_distribution<> normal_dist; std::uniform_real_distribution<> uniform_dist; for (size_t iDim = 0; iDim < NDIM; iDim++) part.getVelocity()[iDim] = normal_dist(Sim->ranGenerator) * sqrtT / std::sqrt(mass); part.getVelocity() //This first line adds a component in the direction of the normal += vNorm * (sqrtT * sqrt(-2.0*log(1.0 - uniform_dist(Sim->ranGenerator)) / mass) //This removes the original normal component -(part.getVelocity() | vNorm) //This adds on the velocity of the wall + d * growthRate) ; return tmpDat; }
void DynNewtonian::streamParticle(Particle &particle, const double &dt) const { particle.getPosition() += particle.getVelocity() * dt; //The Vector copy is required to make sure that the cached //orientation doesn't change during calculation if (hasOrientationData()) orientationData[particle.getID()].orientation = Rodrigues(orientationData[particle.getID()].angularVelocity * dt) * Vector(orientationData[particle.getID()].orientation); }
void DynGravity::streamParticle(Particle &particle, const double &dt) const { bool isDynamic = particle.testState(Particle::DYNAMIC); particle.getPosition() += dt * (particle.getVelocity() + 0.5 * dt * g * isDynamic); particle.getVelocity() += dt * g * isDynamic; if (hasOrientationData()) { orientationData[particle.getID()].orientation = Quaternion::fromRotationAxis(orientationData[particle.getID()].angularVelocity * dt) * orientationData[particle.getID()].orientation ; orientationData[particle.getID()].orientation.normalise(); } }
double Liouvillean::getParticleKineticEnergy(const Particle& part) const { double energy(0); if (Sim->dynamics.BCTypeTest<BCLeesEdwards>()) { const BCLeesEdwards& bc = static_cast<const BCLeesEdwards&>(Sim->dynamics.BCs()); energy += bc.getPeculiarVelocity(part).nrm2() * Sim->dynamics.getSpecies(part).getMass(part.getID()); } else energy += part.getVelocity().nrm2() * Sim->dynamics.getSpecies(part).getMass(part.getID()); if (hasOrientationData()) energy += orientationData[part.getID()].angularVelocity.nrm2() * Sim->dynamics.getSpecies(part).getScalarMomentOfInertia(part.getID()); return 0.5 * energy; }
double Dynamics::getParticleKineticEnergy(const Particle& part) const { double energy(0); if (std::tr1::dynamic_pointer_cast<BCLeesEdwards>(Sim->BCs)) { const BCLeesEdwards& bc = static_cast<const BCLeesEdwards&>(*Sim->BCs); energy += bc.getPeculiarVelocity(part).nrm2() * Sim->species[part]->getMass(part.getID()); } else energy += part.getVelocity().nrm2() * Sim->species[part]->getMass(part.getID()); if (hasOrientationData()) energy += orientationData[part.getID()].angularVelocity.nrm2() * Sim->species[part]->getScalarMomentOfInertia(part.getID()); return 0.5 * energy; }
double Dynamics::getParticleKineticEnergy(const Particle& part) const { const double mass = Sim->species[part]->getMass(part.getID()); double energy(0); if (!std::isinf(mass)) { if (std::dynamic_pointer_cast<BCLeesEdwards>(Sim->BCs)) energy += static_cast<const BCLeesEdwards&>(*Sim->BCs).getPeculiarVelocity(part).nrm2() * mass; else energy += part.getVelocity().nrm2() * mass; } if (hasOrientationData()) { const double I = Sim->species[part]->getScalarMomentOfInertia(part.getID()); if (!std::isinf(I)) energy += I * orientationData[part.getID()].angularVelocity.nrm2(); } return 0.5 * energy; }
/*! \brief Returns the degrees of freedom per particle. */ inline size_t getParticleDOF() const { return NDIM + 2 * hasOrientationData(); }