Palabos程序代码解读

编程入门 行业动态 更新时间:2024-10-11 15:24:13

Palabos<a href=https://www.elefans.com/category/jswz/34/1769335.html style=程序代码解读"/>

Palabos程序代码解读

定义部分

Box3D injectionDomain;
plint startParticleIter = 1200; // Start iterating the particles after this iteration.T particleProbabilityPerCell = 4.e-6; // Per-cell rate of particle injection.
T fluidCompliance = 6.e-5;            // The fluid->particle force is equal to the velocity difference times the fluid-compliance.
Array<T,3> gravity(-3.0e-7, 0., 0.);  // A body force acting on each particle.
T forceAmplitude = 1.e-4;             // Amplitude for the pairwise particle interaction force.
T cutOffLength = 3.5;                 // Cutoff length for particle interaction in lattice units (make sure the interaction force// is negligible at this distance).
plint commEnvelope = (plint)cutOffLength +1;  // Width of communication envelope needed to cope with neighborhoods within the cutoff length.// Must be bigger than blockSize/2.typedef DenseParticleField3D<T,DESCRIPTOR> ParticleFieldT;MultiParticleField3D<ParticleFieldT>* particles    =0;

Particles之间作用的数据处理器。

template<typename T, template<typename U> class Descriptor>
class ParticleInteractionForce : public BoxProcessingFunctional3D
{
public:ParticleInteractionForce(T forceAmplitude_, Array<T,3> gravity_): forceAmplitude(forceAmplitude_),gravity(gravity_){ }// This is the central part of the code, where the particle interaction is defined.virtual void processGenericBlocks(Box3D domain, std::vector<AtomicBlock3D*> blocks){PLB_PRECONDITION( blocks.size()==1 );ParticleField3D<T,Descriptor>& particleField = *dynamic_cast<ParticleField3D<T,Descriptor>*>(blocks[0]);Dot3D offset = particleField.getLocation();std::vector<Particle3D<T,Descriptor>*> particles;std::vector<Particle3D<T,Descriptor>*> neighbors;particleField.findParticles(domain, particles);// Loop over all particles assigned to this data processor.for (pluint iParticle=0; iParticle<particles.size(); ++iParticle) {Particle3D<T,Descriptor>* nonTypeParticle = particles[iParticle];if (nonTypeParticle->getTag()!=0) {  // Exclude the wall particles.VerletParticle3D<T,Descriptor>* particle =dynamic_cast<VerletParticle3D<T,Descriptor>*>(nonTypeParticle);// Compute a neighborhood, in integer coordinates, which contains at least// all particles inside a radius of cutOffLength.Array<T,3> position = particle->getPosition();// Convert global particle coordinates to local coordinates for the domain// treated by this data processor.plint x = util::roundToInt(position[0])-offset.x;plint y = util::roundToInt(position[1])-offset.y;plint z = util::roundToInt(position[2])-offset.z;Box3D neighborhood(x-commEnvelope,x+commEnvelope,y-commEnvelope,y+commEnvelope, z-commEnvelope,z+commEnvelope);neighbors.clear();T rCritical = 2.0;T rCriticalSqr = util::sqr(rCritical);// Use the particle hash to find neighboring particles efficiently.particleField.findParticles(neighborhood, neighbors);Array<T,3> force((T)0.,(T)0.,(T)0.);for (pluint iNeighbor=0; iNeighbor<neighbors.size(); ++iNeighbor) {Array<T,3> r = particle->getPosition() - neighbors[iNeighbor]->getPosition();T rSqr = normSqr(r);if (rSqr<util::sqr(cutOffLength) && rSqr>1.e-6) {// The potential is amplitude*r^{-6} for r>rCritical, and r*amplitude for r<=rCritical.// This is to aovid numerical instability due to huge forces when two// particles come too close (for example in a badly designed initial condition).if (rSqr>rCriticalSqr) {// The potential is r^{-6}, so the force is r^{-7}. An additional r^{-1}// is needed to normalize the vector r.force += r/(rSqr*rSqr*rSqr*rSqr) * forceAmplitude;}else {// Inside a radius of rCritical (in lattice units), the force is constant.T rNorm = std::sqrt(rSqr);force += r/(rNorm*rCritical*rCriticalSqr*rCriticalSqr*rCriticalSqr) * forceAmplitude;}}}// Particle acceleration according to Newton's law.particle->set_a (particle->get_a() + (gravity+force) * particle->get_invRho() );}}}virtual ParticleInteractionForce<T,Descriptor>* clone() const{return new ParticleInteractionForce<T,Descriptor>(*this);}virtual void getTypeOfModification(std::vector<modif::ModifT>& modified) const {modified[0] = modif::dynamicVariables;}
private:T forceAmplitude;Array<T,3> gravity;
};

返回一个圆形区域。

class CircularInjection {
public:CircularInjection ( T radius_, Array<T,3> center_ ): radius(radius_),center(center_){ }bool operator()(Array<T,3> const& pos) const {return (util::sqr(pos[1]-center[1]) + util::sqr(pos[2]-center[2]))< util::sqr(radius);}
private:T radius;Array<T,3> center;
};

建立区域,使其与流场相等。

MultiBlockManagement3D const& latticeManagement(fluidLattice->getMultiBlockManagement());MultiBlockManagement3D particleManagement (latticeManagement.getSparseBlockStructure(),latticeManagement.getThreadAttribution().clone(),commEnvelope, latticeManagement.getRefinementLevel() );// Allocation of the data for the particles. This only allocates the particle-hash// (a grid on which the particles are stored). The particles themselves are injected// later on.particles = new MultiParticleField3D<ParticleFieldT> (particleManagement, defaultMultiBlockPolicy3D().getCombinedStatistics() );

数据处理器的集成。

 // Add a particle on each vertex of the cone wall. These particles don't move, but// they interact with the injected particles, so that these rebounce from the wall.addWallParticlesGeneric<T,DESCRIPTOR,ParticleFieldT>(*particles, *triangleBd);// In the following the data processors for the equations of motion and the// interaction terms are manually added to the particle field. In other situations,// data processors are added implicitly, such as for example the data processors// for the implementation of a boundary condition in the function call// boundaryCondition->insert() above.// Prepare the arguments to be provided to the data processors: the particle field// for Verlet integration, and particles+fluid for the coupling terms.std::vector<MultiBlock3D*> particleArg;particleArg.push_back(particles);std::vector<MultiBlock3D*> particleFluidArg;particleFluidArg.push_back(particles);particleFluidArg.push_back(fluidLattice);// Calls the function advance() on each particle which, for Verlet particles,// updates the position according to v(t+0.5)integrateProcessingFunctional (new AdvanceParticlesEveryWhereFunctional3D<T,DESCRIPTOR>(-1.0),fluidLattice->getBoundingBox(), particleArg, 0);// Compute fluid->particle force (which includes friction).integrateProcessingFunctional (new FluidToParticleCoupling3D<T,DESCRIPTOR>(1.),fluidLattice->getBoundingBox(), particleFluidArg, 1 );// Compute particle->particle and gravity forces, with help of the data processor// ParticleInteractionForce defined above.integrateProcessingFunctional (new ParticleInteractionForce<T,DESCRIPTOR>(forceAmplitude, gravity),fluidLattice->getBoundingBox(), particleArg, 1 );// Integrate the velocity according to the Verlet algorithm.integrateProcessingFunctional (new VerletUpdateVelocitySelective3D<T,DESCRIPTOR>(new util::SelectLargerEqualInt(1)),fluidLattice->getBoundingBox(), particleArg, 1 );// Definition of a domain from which particles will be injected in the flow field.//   The specific domain is close to the inlet of the tube.injectionDomain = Box3D(fluidLattice->getBoundingBox());injectionDomain.x0 = inletCenter[0] +3;injectionDomain.x1 = inletCenter[0] +5;// Definition of an absorbtion domains for the particles, one at the bottom and one at the top.Box3D absorbtionDomain1(fluidLattice->getBoundingBox());absorbtionDomain1.x0 = outletCenter[0] -2;absorbtionDomain1.x1 = outletCenter[0] -2;Box3D absorbtionDomain2(fluidLattice->getBoundingBox());absorbtionDomain2.x0 = inletCenter[0] +1;absorbtionDomain2.x1 = inletCenter[0] +1;// Functional which absorbs the particles in the specified domains, to avoid that they leave the// computational domain.integrateProcessingFunctional (new AbsorbParticlesFunctionalSelective3D<T,DESCRIPTOR>(new util::SelectLargerEqualInt(1)),absorbtionDomain1, particleArg, 0 );integrateProcessingFunctional (new AbsorbParticlesFunctionalSelective3D<T,DESCRIPTOR>(new util::SelectLargerEqualInt(1)),absorbtionDomain2, particleArg, 0 );// Execute all data processors once to start the simulation off with well-defined initial values.particles->executeInternalProcessors();

具体调用,定义particles模板。

// Create a template for the particles to be injected.VerletParticle3D<T,DESCRIPTOR> *particleTemplate=0;particleTemplate = new VerletParticle3D<T,DESCRIPTOR>(1, Array<T,3>(0.,0.,0.));particleTemplate->setFluidCompliance(fluidCompliance);
if (i>=startParticleIter) {std::vector<MultiBlock3D*> particleArg;particleArg.push_back(particles);// Progressively increase the injection rate.T probability = particleProbabilityPerCell;//T probability = particleProbabilityPerCell*(1.+(T)i/6000.);Particle3D<T,DESCRIPTOR>* particle = particleTemplate->clone();// Attribute a color to the first particles by changing their tags (the tags// are written in the VTK file).if (i<=startParticleIter+20000) {particle->setTag(i-startParticleIter);}applyProcessingFunctional (new AnalyticalInjectRandomParticlesFunctional3D<T,DESCRIPTOR,CircularInjection> (particle, probability, CircularInjection(radius/2.0, inletCenter) ),injectionDomain, particleArg );}
 if (i>=startParticleIter) {// Iterate the particles..particles->executeInternalProcessors();}
小总结

定义时选定为DenseParticleField3D,随后的AdvanceParticlesEveryWhereFunctional3D会执行particleField.advanceParticles(particleField.getBoundingBox(), cutOffValue);,找到Dense Particles对应的源码如下:

template<typename T, template<typename U> class Descriptor>
void DenseParticleField3D<T,Descriptor>::advanceParticles(Box3D domain, T cutOffSpeedSqr) {Box3D finalDomain;if( intersect(domain, particleGrid.getBoundingBox(), finalDomain) ){std::vector<std::pair<Dot3D,Particle3D<T,Descriptor>*> > nextCellParticles;for (plint iX=finalDomain.x0; iX<=finalDomain.x1; ++iX) {for (plint iY=finalDomain.y0; iY<=finalDomain.y1; ++iY) {for (plint iZ=finalDomain.z0; iZ<=finalDomain.z1; ++iZ) {std::vector<Particle3D<T,Descriptor>*>& particles = particleGrid.get(iX,iY,iZ);std::vector<Particle3D<T,Descriptor>*> newLocalParticles;for (pluint iParticle=0; iParticle<particles.size(); ++iParticle) {Particle3D<T,Descriptor>* particle = particles[iParticle];Array<T,3> oldPos( particle->getPosition() );particle->advance();if (cutOffSpeedSqr>=T() && normSqr(oldPos-particle->getPosition()) < cutOffSpeedSqr){delete particle;}else {plint newX, newY, newZ;this->computeGridPosition(particle->getPosition(), newX, newY, newZ);if (newX==iX && newY==iY && newZ==iZ) {newLocalParticles.push_back(particle);}else {if (contained(newX,newY,newZ, finalDomain)) {nextCellParticles.push_back(std::make_pair(Dot3D(newX,newY,newZ),particle));}else {delete particle;}}}}newLocalParticles.swap(particles);}}}for (pluint i=0; i<nextCellParticles.size(); ++i) {plint newX = nextCellParticles[i].first.x;plint newY = nextCellParticles[i].first.y;plint newZ = nextCellParticles[i].first.z;particleGrid.get(newX,newY,newZ).push_back(nextCellParticles[i].second);}}
}

遍历指定区域lattice格点,根据区域lattice坐标得到particles格点坐标,遍历该坐标下所有particles,利用advance获得新位置,接下来判断particle新位置是否合理,如果新位置就是该格点则不变,如果位置超出则被删除,如果在指定区域内,则利用nextCellParticles存储新位置,它的构成是<Dot3D,Particle3D<T,Descriptor>*> ,first是一个Dot3D,second是Particles3D。

最后的操作也很直观,即新particle区域位置设定一个particle。particleGrid.get(newX,newY,newZ).push_back(nextCellParticles[i].second);

后续是VerletUpdateVelocitySelective3D:

template<typename T, template<typename U> class Descriptor>
void VerletUpdateVelocitySelective3D<T,Descriptor>::processGenericBlocks (Box3D domain, std::vector<AtomicBlock3D*> blocks )
{PLB_PRECONDITION( blocks.size()==1 );ParticleField3D<T,Descriptor>& particleField =*dynamic_cast<ParticleField3D<T,Descriptor>*>(blocks[0]);std::vector<Particle3D<T,Descriptor>*> found;particleField.findParticles(domain, found);for (pluint iParticle=0; iParticle<found.size(); ++iParticle) {Particle3D<T,Descriptor>* nonTypeParticle = found[iParticle];if ((*tags)(nonTypeParticle->getTag())) {VerletParticle3D<T,Descriptor>* particle =dynamic_cast<VerletParticle3D<T,Descriptor>*>(nonTypeParticle);PLB_ASSERT( particle );Array<T,3> a(particle->get_a());particle->set_v(particle->get_vHalfTime() + (T)0.5*a);PLB_ASSERT( norm(particle->get_v()) < 1. );}}
}

其主要作用:

Array<T,3> a(particle->get_a());
particle->set_v(particle->get_vHalfTime() + (T)0.5*a);

这部分作用与particle3D.hh如下代码几乎一样,比起整个advance,实际上略去了更新坐标的内容,毕竟在这个算例里坐标更新是根据AdvanceParticlesEveryWhereFunctional3D来进行的。

template<typename T, template<typename U> class Descriptor>
void VerletParticle3D<T,Descriptor>::advance() {vHalfTime = v + (T)0.5*a;this->getPosition() += vHalfTime;
}

而这里的a,在particle3D.hh里有如下调用:

Array<T,3> force( (fluidVelocity-this->get_v()) *fluidCompliance);this->set_a(force / this->get_rho());

可以发现这里根据速度,fluidCompliance,rho一起计算的数值。

在particlesInCone算例注释里提到advance是根据v(t+0.5)来更新位置的,我想就是这里的部分吧,在算例自带的数据处理器也有set_a部分,为:

 particle->set_a (
particle->get_a() + (gravity+force) * particle->get_invRho() );

因为是rho*g的形式,我想它应该是作为体积力产生作用的,我最近没有找到particles部分对应的参考文献,所以具体理论暂时不清楚。

这个算例主要的不同点是它自己写了一个particles之间相互作用的数据处理器。

    integrateProcessingFunctional (new ParticleInteractionForce<T,DESCRIPTOR>(forceAmplitude, gravity),fluidLattice->getBoundingBox(), particleArg, 1 );// Integrate the velocity according to the Verlet algorithm.

毕竟是需要包含重力影响,所以就没有使用src里数据处理器的坐标更新方式。

附录:关于a的计算

通过velocity和rhobarJ都可以获得a。

template<typename T, template<typename U> class Descriptor>
void VerletParticle3D<T,Descriptor>::velocityToParticle(TensorField3D<T,3>& velocityField, T scaling)
{Array<T,3> position(this->getPosition());std::vector<Dot3D> pos(8);std::vector<T> weights(8);linearInterpolationCoefficients(velocityField, position, pos, weights);Array<T,3> fluidVelocity;fluidVelocity.resetToZero();for (plint iCell=0; iCell<8; ++iCell) {fluidVelocity += weights[iCell]*velocityField.get(pos[iCell].x,pos[iCell].y,pos[iCell].z)*scaling;}Array<T,3> force( (fluidVelocity-this->get_v()) *fluidCompliance);this->set_a(force / this->get_rho());
}template<typename T, template<typename U> class Descriptor>
void VerletParticle3D<T,Descriptor>::velocityToParticle(NTensorField3D<T>& velocityField, T scaling)
{Array<T,3> position(this->getPosition());std::vector<Dot3D> pos(8);std::vector<T> weights(8);linearInterpolationCoefficients(velocityField, position, pos, weights);Array<T,3> fluidVelocity;fluidVelocity.resetToZero();for (plint iCell=0; iCell<8; ++iCell) {T* data = velocityField.get(pos[iCell].x,pos[iCell].y,pos[iCell].z);fluidVelocity[0] += weights[iCell]*scaling * data[0];fluidVelocity[1] += weights[iCell]*scaling * data[1];fluidVelocity[2] += weights[iCell]*scaling * data[2];}Array<T,3> force( (fluidVelocity-this->get_v()) *fluidCompliance);this->set_a(force / this->get_rho());
}

它的具体过程是先通过流场速度插值计算出流速,如果是velocity方式则接着乘以一个系数scaling,最后乘以fluidCompliance,将其设定为a。

至于为什么要这样做,大家可以自行搜索verlet算法原理。

更多推荐

Palabos程序代码解读

本文发布于:2024-03-04 16:34:25,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1709801.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:程序代码   Palabos

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!