Exemple #1
0
vector<Real> DemFuncs::contactCoordQuantiles(const shared_ptr<DemField>& dem, const vector<Real>& quantiles, const shared_ptr<Node>& node, const AlignedBox3r& box){
	vector<Real> ret;
	ret.reserve(quantiles.size());
	if(quantiles.empty()) return ret;
	vector<Real> coords;
	// count contacts first
	// this count will be off when range is given; it will be larger, so copying is avoided at the cost of some spurious allocation
	size_t N;
	#if 0
		N=boost::range::count_if(*dem->contacts,[&](const shared_ptr<Contact>& C)->bool{ return C->isReal(); });
		coords.reserve(N);
	#endif
	for(const shared_ptr<Contact>& C: *dem->contacts){
		if(!C->isReal()) continue;
		Vector3r p=node?node->glob2loc(C->geom->node->pos):C->geom->node->pos;
		if(!box.isEmpty() && !box.contains(p)) continue;
		coords.push_back(p[2]);
	};
	// no contacts yet, return NaNs
	if(coords.empty()){
		for(size_t i=0; i<quantiles.size(); i++) ret.push_back(NaN);
		return ret;
	}
	boost::range::sort(coords);
	N=coords.size(); // should be the same as N before
	for(Real q: quantiles){
		// clamp the value to 0,1
		q=min(max(0.,q),1.);
		if(isnan(q)) q=0.; // just to make sure
		int i=min((int)((N-1)*q),(int)N-1);
		ret.push_back(coords[i]);
	}
	return ret;
}
Exemple #2
0
Real DemFuncs::porosity(const shared_ptr<DemField>& dem, const shared_ptr<Node>& node, const AlignedBox3r& box){
	Real Vs=0; // solid volume
	Real V=box.volume(); // box volume
	for(const auto& p: *dem->particles){
		if(!p->shape->isA<Sphere>()) continue;
		const Vector3r& pos=p->shape->nodes[0]->pos;
		if(!box.contains(node?node->glob2loc(pos):pos)) continue;
		Vs+=(4/3.)*M_PI*pow(p->shape->cast<Sphere>().radius,3);
	}
	return Vs/V;
}
Exemple #3
0
/* See https://yade-dem.org/doc/yade.utils.html#yade.utils.avgNumInteractions for docs */
Real DemFuncs::coordNumber(const shared_ptr<DemField>& dem, const shared_ptr<Node>& node, const AlignedBox3r& box, int mask, bool skipFree){
	long C2=0; // twice the number of contacts (counted for each participating particle)
	long N0=0; // number of particles without contact (floaters)
	long N1=0; // number of particles with one contact (rattlers)
	long N=0;  // number of all particles
	for(const auto& p: *dem->particles){
		const Vector3r& pos=p->shape->nodes[0]->pos;
		if(mask && (mask&p->mask)==0) continue;
		if(p->shape->nodes[0]->getData<DemData>().isClumped()) throw std::runtime_error("Not yet implemented for clumps.");
		if(!box.contains(node?node->glob2loc(pos):pos)) continue;
		int n=p->countRealContacts();
		if(n==0) N0++;
		else if(n==1) N1++;
		N++;
		C2+=n;
	}
	if(skipFree) return (C2-N1)*1./(N-N0-N1);
	else return C2*1./N;
}
Exemple #4
0
vector<Vector2r> DemFuncs::boxPsd(const Scene* scene, const DemField* dem, const AlignedBox3r& box, bool mass, int num, int mask, Vector2r rRange){
	bool haveBox=!isnan(box.min()[0]) && !isnan(box.max()[0]);
	return psd(
		*dem->particles|boost::adaptors::filtered([&](const shared_ptr<Particle>&p){ return p && p->shape && p->shape->nodes.size()==1 && (mask?(p->mask&mask):true) && (bool)(dynamic_pointer_cast<woo::Sphere>(p->shape)) && (haveBox?box.contains(p->shape->nodes[0]->pos):true); }),
		/*cumulative*/true,/*normalize*/true,
		num,
		rRange,
		/*diameter getter*/[](const shared_ptr<Particle>&p) ->Real { return 2.*p->shape->cast<Sphere>().radius; },
		/*weight getter*/[&](const shared_ptr<Particle>&p) -> Real{ return mass?p->shape->nodes[0]->getData<DemData>().mass:1.; }
	);
}
Exemple #5
0
void RandomInlet::run(){
	DemField* dem=static_cast<DemField*>(field.get());
	if(!generator) throw std::runtime_error("RandomInlet.generator==None!");
	if(materials.empty()) throw std::runtime_error("RandomInlet.materials is empty!");
	if(collideExisting){
		if(!collider){
		for(const auto& e: scene->engines){ collider=dynamic_pointer_cast<Collider>(e); if(collider) break; }
		if(!collider) throw std::runtime_error("RandomInlet: no Collider found within engines (needed for collisions detection with already existing particles; if you don't need that, set collideExisting=False.)");
		}
		if(dynamic_pointer_cast<InsertionSortCollider>(collider)) static_pointer_cast<InsertionSortCollider>(collider)->forceInitSort=true;
	}
	if(isnan(massRate)) throw std::runtime_error("RandomInlet.massRate must be given (is "+to_string(massRate)+"); if you want to generate as many particles as possible, say massRate=0.");
	if(massRate<=0 && maxAttempts==0) throw std::runtime_error("RandomInlet.massFlowRate<=0 (no massFlowRate prescribed), but RandomInlet.maxAttempts==0. (unlimited number of attempts); this would cause infinite loop.");
	if(maxAttempts<0){
		std::runtime_error("RandomInlet.maxAttempts must be non-negative. Negative value, leading to meaking engine dead, is achieved by setting atMaxAttempts=RandomInlet.maxAttDead now.");
	}
	spheresOnly=generator->isSpheresOnly();
	padDist=generator->padDist();
	if(isnan(padDist) || padDist<0) throw std::runtime_error(generator->pyStr()+".padDist(): returned invalid value "+to_string(padDist));

	// as if some time has already elapsed at the very start
	// otherwise mass flow rate is too big since one particle during Δt exceeds it easily
	// equivalent to not running the first time, but without time waste
	if(stepPrev==-1 && stepPeriod>0) stepPrev=-stepPeriod; 
	long nSteps=scene->step-stepPrev;
	// to be attained in this step;
	stepGoalMass+=massRate*scene->dt*nSteps; // stepLast==-1 if never run, which is OK
	vector<AlignedBox3r> genBoxes; // of particles created in this step
	vector<shared_ptr<Particle>> generated;
	Real stepMass=0.;

	SpherePack spheres;
	//
	if(spheresOnly){
		spheres.pack.reserve(dem->particles->size()*1.2); // something extra for generated particles
		// HACK!!!
		bool isBox=dynamic_cast<BoxInlet*>(this);
		AlignedBox3r box;
		if(isBox){ box=this->cast<BoxInlet>().box; }
		for(const auto& p: *dem->particles){
			if(p->shape->isA<Sphere>() && (!isBox || box.contains(p->shape->nodes[0]->pos))) spheres.pack.push_back(SpherePack::Sph(p->shape->nodes[0]->pos,p->shape->cast<Sphere>().radius));
		}
	}

	while(true){
		// finished forever
		if(everythingDone()) return;

		// finished in this step
		if(massRate>0 && mass>=stepGoalMass) break;

		shared_ptr<Material> mat;
		Vector3r pos=Vector3r::Zero();
		Real diam;
		vector<ParticleGenerator::ParticleAndBox> pee;
		int attempt=-1;
		while(true){
			attempt++;
			/***** too many tries, give up ******/
			if(attempt>=maxAttempts){
				generator->revokeLast(); // last particle could not be placed
				if(massRate<=0){
					LOG_DEBUG("maxAttempts="<<maxAttempts<<" reached; since massRate is not positive, we're done in this step");
					goto stepDone;
				}
				switch(atMaxAttempts){
					case MAXATT_ERROR: throw std::runtime_error("RandomInlet.maxAttempts reached ("+lexical_cast<string>(maxAttempts)+")"); break;
					case MAXATT_DEAD:{
						LOG_INFO("maxAttempts="<<maxAttempts<<" reached, making myself dead.");
						this->dead=true;
						return;
					}
					case MAXATT_WARN: LOG_WARN("maxAttempts "<<maxAttempts<<" reached before required mass amount was generated; continuing, since maxAttemptsError==False"); break;
					case MAXATT_SILENT: break;
					default: throw std::invalid_argument("Invalid value of RandomInlet.atMaxAttempts="+to_string(atMaxAttempts)+".");
				}
			}
			/***** each maxAttempts/attPerPar, try a new particles *****/	
			if((attempt%(maxAttempts/attemptPar))==0){
				LOG_DEBUG("attempt "<<attempt<<": trying with a new particle.");
				if(attempt>0) generator->revokeLast(); // if not at the beginning, revoke the last particle

				// random choice of material with equal probability
				if(materials.size()==1) mat=materials[0];
				else{ 
					size_t i=max(size_t(materials.size()*Mathr::UnitRandom()),materials.size()-1);;
					mat=materials[i];
				}
				// generate a new particle
				std::tie(diam,pee)=(*generator)(mat,scene->time);
				assert(!pee.empty());
				LOG_TRACE("Placing "<<pee.size()<<"-sized particle; first component is a "<<pee[0].par->getClassName()<<", extents from "<<pee[0].extents.min().transpose()<<" to "<<pee[0].extents.max().transpose());
			}

			pos=randomPosition(diam,padDist); // overridden in child classes
			LOG_TRACE("Trying pos="<<pos.transpose());
			for(const auto& pe: pee){
				// make translated copy
				AlignedBox3r peBox(pe.extents); peBox.translate(pos); 
				// box is not entirely within the factory shape
				if(!validateBox(peBox)){ LOG_TRACE("validateBox failed."); goto tryAgain; }

				const shared_ptr<woo::Sphere>& peSphere=dynamic_pointer_cast<woo::Sphere>(pe.par->shape);

				if(spheresOnly){
					if(!peSphere) throw std::runtime_error("RandomInlet.spheresOnly: is true, but a nonspherical particle ("+pe.par->shape->pyStr()+") was returned by the generator.");
					Real r=peSphere->radius;
					Vector3r subPos=peSphere->nodes[0]->pos;
					for(const auto& s: spheres.pack){
						// check dist && don't collide with another sphere from this clump
						// (abuses the *num* counter for clumpId)
						if((s.c-(pos+subPos)).squaredNorm()<pow(s.r+r,2)){
							LOG_TRACE("Collision with a particle in SpherePack (a particle generated in this step).");
							goto tryAgain;
						}
					}
					// don't add to spheres until all particles will have been checked for overlaps (below)
				} else {
					// see intersection with existing particles
					bool overlap=false;
					if(collideExisting){
						vector<Particle::id_t> ids=collider->probeAabb(peBox.min(),peBox.max());
						for(const auto& id: ids){
							LOG_TRACE("Collider reports intersection with #"<<id);
							if(id>(Particle::id_t)dem->particles->size() || !(*dem->particles)[id]) continue;
							const shared_ptr<Shape>& sh2((*dem->particles)[id]->shape);
							// no spheres, or they are too close
							if(!peSphere || !sh2->isA<woo::Sphere>() || 1.1*(pos-sh2->nodes[0]->pos).squaredNorm()<pow(peSphere->radius+sh2->cast<Sphere>().radius,2)) goto tryAgain;
						}
					}

					// intersection with particles generated by ourselves in this step
					for(size_t i=0; i<genBoxes.size(); i++){
						overlap=(genBoxes[i].squaredExteriorDistance(peBox)==0.);
						if(overlap){
							const auto& genSh(generated[i]->shape);
							// for spheres, try to compute whether they really touch
							if(!peSphere || !genSh->isA<Sphere>() || (pos-genSh->nodes[0]->pos).squaredNorm()<pow(peSphere->radius+genSh->cast<Sphere>().radius,2)){
								LOG_TRACE("Collision with "<<i<<"-th particle generated in this step.");
								goto tryAgain;
							}
						}
					}
				}
			}
			LOG_DEBUG("No collision (attempt "<<attempt<<"), particle will be created :-) ");
			if(spheresOnly){
				// num will be the same for all spheres within this clump (abuse the *num* counter)
				for(const auto& pe: pee){
					Vector3r subPos=pe.par->shape->nodes[0]->pos;
					Real r=pe.par->shape->cast<Sphere>().radius;
					spheres.pack.push_back(SpherePack::Sph((pos+subPos),r,/*clumpId*/(pee.size()==1?-1:num)));
				}
			}
			break;
			tryAgain: ; // try to position the same particle again
		}

		// particle was generated successfully and we have place for it
		for(const auto& pe: pee){
			genBoxes.push_back(AlignedBox3r(pe.extents).translate(pos));
			generated.push_back(pe.par);
		}

		num+=1;

		#ifdef WOO_OPENGL			
			Real color_=isnan(color)?Mathr::UnitRandom():color;
		#endif
		if(pee.size()>1){ // clump was generated
			//LOG_WARN("Clumps not yet tested properly.");
			vector<shared_ptr<Node>> nn;
			for(auto& pe: pee){
				auto& p=pe.par;
				p->mask=mask;
				#ifdef WOO_OPENGL
					assert(p->shape);
					if(color_>=0) p->shape->color=color_; // otherwise keep generator-assigned color
				#endif
				if(p->shape->nodes.size()!=1) LOG_WARN("Adding suspicious clump containing particle with more than one node (please check, this is perhaps not tested");
				for(const auto& n: p->shape->nodes){
					nn.push_back(n);
					n->pos+=pos;
				}
				dem->particles->insert(p);
			}
			shared_ptr<Node> clump=ClumpData::makeClump(nn,/*no central node pre-given*/shared_ptr<Node>(),/*intersection*/false);
			auto& dyn=clump->getData<DemData>();
			if(shooter) (*shooter)(clump);
			if(scene->trackEnergy) scene->energy->add(-DemData::getEk_any(clump,true,true,scene),"kinInlet",kinEnergyIx,EnergyTracker::ZeroDontCreate);
			if(dyn.angVel!=Vector3r::Zero()){
				throw std::runtime_error("pkg/dem/RandomInlet.cpp: generated particle has non-zero angular velocity; angular momentum should be computed so that rotation integration is correct, but it was not yet implemented.");
				// TODO: compute initial angular momentum, since we will (very likely) use the aspherical integrator
			}
			ClumpData::applyToMembers(clump,/*reset*/false); // apply velocity
			#ifdef WOO_OPENGL
				boost::mutex::scoped_lock lock(dem->nodesMutex);
			#endif
			dyn.linIx=dem->nodes.size();
			dem->nodes.push_back(clump);

			mass+=clump->getData<DemData>().mass;
			stepMass+=clump->getData<DemData>().mass;
		} else {
			auto& p=pee[0].par;
			p->mask=mask;
			assert(p->shape);
			#ifdef WOO_OPENGL
				if(color_>=0) p->shape->color=color_;
			#endif
			assert(p->shape->nodes.size()==1); // if this fails, enable the block below
			const auto& node0=p->shape->nodes[0];
			assert(node0->pos==Vector3r::Zero());
			node0->pos+=pos;
			auto& dyn=node0->getData<DemData>();
			if(shooter) (*shooter)(node0);
			if(scene->trackEnergy) scene->energy->add(-DemData::getEk_any(node0,true,true,scene),"kinInlet",kinEnergyIx,EnergyTracker::ZeroDontCreate);
			mass+=dyn.mass;
			stepMass+=dyn.mass;
			assert(node0->hasData<DemData>());
			dem->particles->insert(p);
			#ifdef WOO_OPENGL
				boost::mutex::scoped_lock lock(dem->nodesMutex);
			#endif
			dyn.linIx=dem->nodes.size();
			dem->nodes.push_back(node0);
			// handle multi-nodal particle (unused now)
			#if 0
				// TODO: track energy of the shooter
				// in case the particle is multinodal, apply the same to all nodes
				if(shooter) shooter(p.shape->nodes[0]);
				const Vector3r& vel(p->shape->nodes[0]->getData<DemData>().vel); const Vector3r& angVel(p->shape->nodes[0]->getData<DemData>().angVel);
				for(const auto& n: p.shape->nodes){
					auto& dyn=n->getData<DemData>();
					dyn.vel=vel; dyn.angVel=angVel;
					mass+=dyn.mass;

					n->linIx=dem->nodes.size();
					dem->nodes.push_back(n);
				}
			#endif
		}
	};

	stepDone:
	setCurrRate(stepMass/(nSteps*scene->dt));
}