← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 2136: 1. Add toy grid-based collider (very slow)

 

------------------------------------------------------------
revno: 2136
committer: Václav Šmilauer <eudoxos@xxxxxxxx>
branch nick: trunk
timestamp: Fri 2010-04-09 13:26:20 +0200
message:
  1. Add toy grid-based collider (very slow)
added:
  pkg/dem/Engine/GlobalEngine/FlatGridCollider.cpp
  pkg/dem/Engine/GlobalEngine/FlatGridCollider.hpp
  scripts/test/flat-collider.py


--
lp:yade
https://code.launchpad.net/~yade-dev/yade/trunk

Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-dev/yade/trunk/+edit-subscription
=== added file 'pkg/dem/Engine/GlobalEngine/FlatGridCollider.cpp'
--- pkg/dem/Engine/GlobalEngine/FlatGridCollider.cpp	1970-01-01 00:00:00 +0000
+++ pkg/dem/Engine/GlobalEngine/FlatGridCollider.cpp	2010-04-09 11:26:20 +0000
@@ -0,0 +1,105 @@
+// 2010 © Václav Šmilauer <eudoxos@xxxxxxxx> 
+#include<yade/pkg-dem/FlatGridCollider.hpp>
+#include<yade/core/Scene.hpp>
+
+#include<yade/pkg-common/Sphere.hpp>
+#include<yade/pkg-dem/NewtonIntegrator.hpp>
+//#include<yade/pkg-common/Facet.hpp>
+
+YADE_PLUGIN((FlatGridCollider));
+CREATE_LOGGER(FlatGridCollider);
+
+bool FlatGridCollider::isActivated(){
+	// keep interactions trequested for deletion as potential (forget removal requests)
+	scene->interactions->clearPendingErase();
+	if(!newton) return true;
+	// handle verlet distance
+	fastestBodyMaxDist+=sqrt(newton->maxVelocitySq)*scene->dt;
+	if(fastestBodyMaxDist>verletDist) return true;
+	return false;
+}
+
+void FlatGridCollider::action(){
+	if(!newton){
+		FOREACH(const shared_ptr<Engine>& e, scene->engines){ newton=dynamic_pointer_cast<NewtonIntegrator>(e); if(newton) break; }
+		if(!newton){ throw runtime_error("FlatGridCollider: Unable to find NewtonIntegrator in engines."); }
+	}
+	fastestBodyMaxDist=0;
+	// make interaction loop delete unseen potential interactions
+	scene->interactions->iterColliderLastRun=scene->currentIteration;
+	// adjust grid if necessary
+	updateGrid();
+	FOREACH(const shared_ptr<Body>& b, *scene->bodies){
+		if(!b) continue; // deleted bodies
+		updateBodyCells(b);
+	}
+	updateCollisions();
+}
+
+
+// called from the ctor
+void FlatGridCollider::initIndices(){
+	sphereIdx=facetIdx=wallIdx=boxIdx=-1;
+	sphereIdx=Sphere::getClassIndexStatic();
+	LOG_DEBUG("sphereIdx="<<sphereIdx);
+}
+
+void FlatGridCollider::updateGrid(){
+	// checks
+	if(step<=0) throw std::runtime_error("FlatGridCollider::step must be positive.");
+	Vector3r aabbSize=aabbMax-aabbMin; if(aabbSize[0]<=0 || aabbSize[1]<=0 || aabbSize[2]<=0) throw std::runtime_error("FlatGridCollider::{aabbMin,aabbMax} must give positive volume.");
+	// compute new size
+	grid.step=step;
+	grid.mn=aabbMin;
+	for(int i=0;i<3;i++)grid.size[i]=(int)ceil((aabbMax[i]-aabbMin[i])/step);
+	grid.mx=aabbMin+Vector3r(grid.size[0]*step,grid.size[1]*step,grid.size[2]*step);
+	size_t len=grid.size[0]*grid.size[1]*grid.size[2];
+	// reset grid data
+	grid.data.clear(); grid.data.resize(len); // delete and recreate; could be optimized somehow
+	LOG_DEBUG("New grid "<<grid.size[0]<<"×"<<grid.size[1]<<"×"<<grid.size[2]<<"="<<len<<" cells, step "<<step<<"; spans ("<<grid.mn<<")--("<<grid.mx<<").");
+}
+
+void FlatGridCollider::updateBodyCells(const shared_ptr<Body>& b){
+	if(!b->shape) return; const Shape* shape(b->shape.get());
+	// Sphere
+	if(shape->getClassIndex()==sphereIdx){
+		Real r=static_cast<const Sphere*>(shape)->radius+verletDist;
+		const Vector3r& C=b->state->pos;
+		// create "bounding cells" and traverse them one by one; integrized coords can be _outside_ grid, they will be forced to closest cells before insertion
+		Vector3i cMn=grid.pt2int(C-Vector3r(r,r,r)), cMx=grid.pt2int(C+Vector3r(r,r,r)), cC=grid.pt2int(C), cPt;
+		LOG_TRACE("Sphere #"<<b->id<<", C="<<C<<", cMn="<<cMn<<", cC="<<cC<<", cMx="<<cMx);
+		for(cPt[0]=cMn[0]; cPt[0]<=cMx[0]; cPt[0]++) for(cPt[1]=cMn[1]; cPt[1]<=cMx[1]; cPt[1]++) for(cPt[2]=cMn[2]; cPt[2]<=cMx[2]; cPt[2]++){
+			// find closest cell point (to cC); keep coordinate in same line (cPt[i]==cC[i]); take upper/lower point in lower/upper lines (cPt[i]<cC[i])
+			Vector3r ccp;
+			for(int i=0;i<3;i++) ccp[i]=(cPt[i]==cC[i] ? C[i] : (grid.mn[i]+grid.step*(cPt[i]+(cPt[i]<cC[i] ? 1 : 0))));
+			if((C-ccp).SquaredLength()<=r*r){ // closest cell point it inside the spehre; add the sphere to cell at cell position
+				Vector3i cPtIn(grid.fitGrid(cPt));
+				// perhaps slower, but inserts each body only once into the cell (meaningful if outside grid: multiple integer coords coolapse in one cell)
+				{ Grid::idVector& vv=grid(cPtIn); if(vv.size()==0 || *(vv.rbegin())!=b->id) vv.push_back(b->id);}
+				//grid(cPtIn).push_back(b->id);
+				LOG_TRACE("Added sphere #"<<b->id<<" to cell ("<<cPtIn<<")←["<<cPt<<"]; center ("<<C<<"), closest ("<<ccp<<"), dist "<<(C-ccp).Length());
+			}
+		}
+		return;
+	}
+	throw std::runtime_error("FlatGridCollider::updateBodyCells does not handle Shape of type "+shape->getClassName()+"!");
+}
+
+void FlatGridCollider::updateCollisions(){
+	shared_ptr<InteractionContainer>& intrs=scene->interactions;
+	const long& iter=scene->currentIteration;
+	// create interactions for all combinations of bodies within one cell
+	FOREACH(const Grid::idVector& v, grid.data){
+		size_t sz=v.size();
+		for(size_t i=0; i<sz; i++) for(size_t j=i+1; j<sz; j++){
+			body_id_t id1=v[i], id2=v[j];
+			if(id1==id2) continue; // at grid boundary, it is possible to have one body more times in one cell
+			const shared_ptr<Interaction>& I=intrs->find(id1,id2);
+			if(I){ I->iterLastSeen=iter; continue; }
+			// no interaction yet
+			if(!Collider::mayCollide(Body::byId(id1,scene).get(),Body::byId(id2,scene).get())) continue;
+			intrs->insert(shared_ptr<Interaction>(new Interaction(id1,id2)));
+			LOG_TRACE("Created new interaction #"<<id1<<"+#"<<id2);
+		}
+	}
+}

=== added file 'pkg/dem/Engine/GlobalEngine/FlatGridCollider.hpp'
--- pkg/dem/Engine/GlobalEngine/FlatGridCollider.hpp	1970-01-01 00:00:00 +0000
+++ pkg/dem/Engine/GlobalEngine/FlatGridCollider.hpp	2010-04-09 11:26:20 +0000
@@ -0,0 +1,46 @@
+// 2009 © Václav Šmilauer <eudoxos@xxxxxxxx> 
+#include<vector>
+#include<yade/core/Collider.hpp>
+class NewtonIntegrator;
+class FlatGridCollider: public Collider{
+	struct Grid{
+		typedef std::vector<body_id_t> idVector;
+		Vector3i size;
+		Vector3r mn, mx;
+		Real step;
+		// convert point into its integral coordinates (can be outside grid, use fitGrid to coords inside)
+		Vector3i pt2int(const Vector3r& pt){ Vector3i ret; for(int i=0;i<3;i++)ret[i]=floor((pt[i]-mn[1])/step); return ret; }
+		std::vector<idVector> data;
+		// force integral coordinate inside (0,sz-1)
+		int fit(int i, int sz) const { return max(0,min(i,sz-1)); }
+		Vector3i fitGrid(const Vector3i& v){ return Vector3i(fit(v[0],size[0]),fit(v[1],size[1]),fit(v[2],size[2])); }
+		// linearize x,y,z → n in data vector
+		size_t lin(int x, int y, int z) const { return fit(x,size[0])+size[0]*fit(y,size[1])+size[0]*size[1]*fit(z,size[2]); }
+		// return vector of ids at (x,y,z)
+		idVector& operator()(int x, int y, int z){ return data[lin(x,y,z)];}
+		idVector& operator()(const Vector3i& v){ return data[lin(v[0],v[1],v[2])];}
+		const idVector& operator()(int x, int y, int z) const { return data[lin(x,y,z)];}
+	};
+	Grid grid;
+	int sphereIdx, facetIdx, wallIdx, boxIdx;
+	// needed for maxVelSq
+	shared_ptr<NewtonIntegrator> newton;
+	// track maximum distance of fastest body, at every step in isActivated
+	Real fastestBodyMaxDist;
+	void initIndices();
+	void updateGrid();
+	void updateBodyCells(const shared_ptr<Body>& b);
+	void updateCollisions();
+	virtual void action();
+	virtual bool isActivated();
+	DECLARE_LOGGER;
+
+	YADE_CLASS_BASE_DOC_ATTRS_CTOR(FlatGridCollider,Collider,"Non-optimized grid collider, storing grid as dense flat array. Each body is assigned to (possibly multiple) cells, which are arranged in regular grid between *aabbMin* and *aabbMax*, with cell size *step* (same in all directions). Bodies outsize (*aabbMin*, *aabbMax*) are handled gracefully, assigned to closest cells (this will create spurious potential interactions). *verletDist* determines how much is each body enlarged to avoid collision detection at every step.\n\n.. note::\n\tThis collider keeps all cells in linear memory array, therefore will be memory-inefficient for sparse simulations.\n\n.. warning::\n\t:yref:`Body::bound` objects are not used, :yref:`BoundFunctors<BoundFunctor>` are not used either: assigning cells to bodies is hard-coded internally. Currently handles :yref:`Shapes<Shape>` are: :yref:`Sphere`.\n\n.. note::\n\tPeriodic boundary is not handled (yet).\n\n",
+		((Real,verletDist,0,"Length by which enlarge space occupied by each particle; avoids running collision detection at every step."))
+		((Vector3r,aabbMin,Vector3r::ZERO,"Lower corner of grid."))
+		((Vector3r,aabbMax,Vector3r::ZERO,"Upper corner of grid (approximate, might be rouded up to *minStep*."))
+		((Real,step,0,"Step in the grid (cell size)")),
+		initIndices();
+	);
+};
+REGISTER_SERIALIZABLE(FlatGridCollider);

=== added file 'scripts/test/flat-collider.py'
--- scripts/test/flat-collider.py	1970-01-01 00:00:00 +0000
+++ scripts/test/flat-collider.py	2010-04-09 11:26:20 +0000
@@ -0,0 +1,28 @@
+from yade import log,utils,pack,timing
+#log.setLevel('FlatGridCollider',log.TRACE)
+#O.bodies.append([	utils.sphere((0.2,0,0),.5,dynamic=False), utils.sphere((0.2,0.0,1.01),.5), ])
+O.bodies.append(pack.regularHexa(pack.inAlignedBox((0,0,0),(10,10,1)),radius=.5,gap=0,dynamic=False))
+O.bodies.append(pack.regularOrtho(pack.inAlignedBox((3,3,3),(7,7,4)),radius=.05,gap=0))
+O.engines=[
+	ForceResetter(),
+	FlatGridCollider(step=.2,aabbMin=(0,0,0),aabbMax=(10,10,5),verletDist=0.005),
+	#BoundDispatcher([Bo1_Sphere_Aabb()]), InsertionSortCollider(sweepLength=0.005),
+	InteractionDispatchers(
+		[Ig2_Sphere_Sphere_Dem3DofGeom()],
+		[Ip2_FrictMat_FrictMat_FrictPhys()],
+		[Law2_Dem3DofGeom_FrictPhys_Basic()],
+	),
+	GravityEngine(gravity=[0,0,-10]),
+	NewtonIntegrator(damping=0.4),
+]
+O.dt=.6*utils.PWaveTimeStep()
+O.saveTmp()
+#O.step()
+#while True:
+#	O.step()
+#	if len(O.interactions)>0 or O.bodies[1].state.pos[2]<.97: break
+O.timingEnabled=True
+O.run(5000,True)
+timing.stats()
+import sys
+#sys.exit(0)


Follow ups