← Back to team overview

yade-dev team mailing list archive

[svn] r1690 - in trunk: core/containers extra gui gui/py pkg/common pkg/common/Engine/MetaEngine

 

Author: eudoxos
Date: 2009-02-25 15:25:55 +0100 (Wed, 25 Feb 2009)
New Revision: 1690

Added:
   trunk/core/containers/InteractionHashMap.cpp
   trunk/core/containers/InteractionHashMap.hpp
   trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp
   trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.hpp
Modified:
   trunk/extra/Brefcom.cpp
   trunk/gui/SConscript
   trunk/gui/py/timing.py
   trunk/gui/py/utils.py
   trunk/gui/py/yade-multi
   trunk/gui/py/yadeControl.cpp
   trunk/pkg/common/SConscript
Log:
1. New InteractionDispatchers class that has one common loop for InteractionGeometryMetaEngine, InteractionPhysicsMetaEngine and ConstitutiveLawDispatcher. It can be used from python like this:
  
 O.engines=[..., InteractionDispatchers([EngineUnit('geomFunctor1'),EngineUnit('geomFunctor2')],[EngineUnit('physFunctor1'),...],[EngineUnit('ConstitutiveLaw1'),...])

Gives about 5% of speedups, but not yet measured exactly.

2. Fix InteractionHashMap. I forgot to svn add... Sorry
3. Fix all other compilation problems with/without NO_BEX, with/without openmp (hopefully.
4. yade-multi now passes parameters that are all uppercase (with numbers and underscores) as env. vars to the process (think OMP_NUM_THREADS).


Added: trunk/core/containers/InteractionHashMap.cpp
===================================================================
--- trunk/core/containers/InteractionHashMap.cpp	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/core/containers/InteractionHashMap.cpp	2009-02-25 14:25:55 UTC (rev 1690)
@@ -0,0 +1,169 @@
+/*************************************************************************
+*  Copyright (C) 2004 by Olivier Galizzi                                 *
+*  olivier.galizzi@xxxxxxx                                               *
+*                                                                        *
+*  This program is free software; it is licensed under the terms of the  *
+*  GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+
+#include "InteractionHashMap.hpp"
+
+InteractionHashMapIterator::InteractionHashMapIterator() : InteractionContainerIterator()
+{
+
+}
+
+
+InteractionHashMapIterator::~InteractionHashMapIterator()
+{
+
+}
+
+
+bool InteractionHashMapIterator::isDifferent(const InteractionContainerIterator& i)
+{
+	return (hmii != static_cast<const InteractionHashMapIterator&>(i).hmii );
+}
+
+
+void InteractionHashMapIterator::increment()
+{
+	++hmii;
+}
+
+
+void InteractionHashMapIterator::affect(const InteractionContainerIterator& i)
+{
+	hmii = static_cast<const InteractionHashMapIterator&>(i).hmii;
+}
+
+
+shared_ptr<Interaction> InteractionHashMapIterator::getValue()
+{
+	return (*hmii).second;
+}
+
+
+shared_ptr<InteractionContainerIterator> InteractionHashMapIterator::createPtr()
+{
+	return shared_ptr<InteractionContainerIterator>(new InteractionHashMapIterator());
+}
+
+
+/***********************************************************************/
+/***********************************************************************/
+/***********************************************************************/
+/***********************************************************************/
+
+InteractionHashMap::InteractionHashMap()
+{
+	clear();
+}
+
+
+InteractionHashMap::~InteractionHashMap()
+{
+}
+
+
+bool InteractionHashMap::insert(shared_ptr<Interaction>& i)
+{
+	boost::mutex::scoped_lock lock(drawloopmutex);
+
+	body_id_t id1 = i->getId1();
+	body_id_t id2 = i->getId2();
+	if (id1>id2)
+		swap(id1,id2);
+
+	return interactions.insert( IHashMap::value_type( pair<body_id_t,body_id_t>(id1,id2) , i )).second;
+}
+
+
+bool InteractionHashMap::insert(body_id_t id1,body_id_t id2)
+{
+	shared_ptr<Interaction> i(new Interaction(id1,id2) );
+	return insert(i);	
+}
+
+
+void InteractionHashMap::clear()
+{
+	boost::mutex::scoped_lock lock(drawloopmutex);
+
+	interactions.clear();
+}
+
+
+bool InteractionHashMap::erase(body_id_t id1,body_id_t id2)
+{
+	boost::mutex::scoped_lock lock(drawloopmutex);
+
+	if (id1>id2)
+		swap(id1,id2);
+
+	unsigned int oldSize = interactions.size();
+	pair<body_id_t,body_id_t> p(id1,id2);
+	unsigned int size = interactions.erase(p);
+
+	return size!=oldSize;
+
+}
+
+
+const shared_ptr<Interaction>& InteractionHashMap::find(body_id_t id1,body_id_t id2)
+{
+	if (id1>id2)
+		swap(id1,id2);
+
+	IHashMap::iterator hmii = interactions.find(pair<body_id_t,body_id_t>(id1,id2));
+	if (hmii!=interactions.end())
+		return (*hmii).second;
+	else
+	{
+		empty = shared_ptr<Interaction>(); 
+		return empty;
+	}
+}
+
+
+InteractionContainer::iterator InteractionHashMap::begin()
+{
+	shared_ptr<InteractionHashMapIterator> it(new InteractionHashMapIterator());
+	it->hmii   = interactions.begin();
+
+	return InteractionContainer::iterator(it);
+}
+
+
+InteractionContainer::iterator InteractionHashMap::end()
+{
+	shared_ptr<InteractionHashMapIterator> it(new InteractionHashMapIterator());
+	it->hmii   = interactions.end();
+
+	return InteractionContainer::iterator(it);
+}
+
+
+// void InteractionHashMap::eraseCurrentAndGotoNextPotential()
+// {
+// 	if (notAtEnd())
+// 	{
+// 		IHashMap::iterator tmpHmii=hmii;
+// 		++hmii;
+// 		interactions.erase(tmpHmii);
+// 	}
+// }
+// 
+// void InteractionHashMap::eraseCurrentAndGotoNext()
+// {
+// 	IHashMap::iterator tmpHmii=hmii;	
+// 	while (notAtEnd() && !((*hmii).second->isReal))
+// 		++hmii;	
+// 	interactions.erase(tmpHmii);
+// }
+
+unsigned int InteractionHashMap::size()
+{
+	return interactions.size();
+}
+YADE_PLUGIN();

Added: trunk/core/containers/InteractionHashMap.hpp
===================================================================
--- trunk/core/containers/InteractionHashMap.hpp	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/core/containers/InteractionHashMap.hpp	2009-02-25 14:25:55 UTC (rev 1690)
@@ -0,0 +1,83 @@
+/*************************************************************************
+*  Copyright (C) 2004 by Olivier Galizzi                                 *
+*  olivier.galizzi@xxxxxxx                                               *
+*                                                                        *
+*  This program is free software; it is licensed under the terms of the  *
+*  GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+
+#pragma once
+
+#include<yade/core/InteractionContainer.hpp>
+#include<yade/core/Interaction.hpp>
+#include<ext/hash_map>
+#include<vector>
+#include"InteractionHashMap.hpp"
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+
+
+struct eqPair
+{
+	bool operator()(const pair<body_id_t,body_id_t>& p1, const pair<body_id_t,body_id_t>& p2) const
+	{
+		return (p1.first==p2.first && p1.second==p2.second);
+	}
+};
+
+struct hashPair
+{
+	unsigned int operator()(const pair<body_id_t,body_id_t>& p) const
+	{
+		return ((unsigned int)p.first+(unsigned int)p.second)%182501621;
+	}
+};
+
+typedef hash_map<pair<body_id_t,body_id_t>, shared_ptr<Interaction>, hashPair, eqPair > IHashMap;
+
+class InteractionHashMap : public InteractionContainer
+{
+	private :
+		IHashMap interactions;
+		shared_ptr<Interaction> empty;
+
+	public :
+		InteractionHashMap();
+		virtual ~InteractionHashMap();
+
+		virtual bool insert(body_id_t id1,body_id_t id2);
+		virtual bool insert(shared_ptr<Interaction>& i);
+		virtual void clear();
+		virtual bool erase(body_id_t id1,body_id_t id2);
+		virtual const shared_ptr<Interaction>& find(body_id_t id1,body_id_t id2);
+	
+		virtual InteractionContainer::iterator begin();
+	        virtual InteractionContainer::iterator end();
+	
+		virtual unsigned int size();
+
+	REGISTER_CLASS_NAME(InteractionHashMap);
+	REGISTER_BASE_CLASS_NAME(InteractionContainer);
+};
+
+class InteractionHashMapIterator : public InteractionContainerIterator 
+{
+	public :
+		IHashMap::iterator hmii;
+
+		InteractionHashMapIterator();
+		~InteractionHashMapIterator();
+
+		virtual bool isDifferent(const InteractionContainerIterator& i);
+		virtual void affect(const InteractionContainerIterator& i);
+		virtual void increment();
+		virtual shared_ptr<Interaction> getValue();
+		virtual shared_ptr<InteractionContainerIterator> createPtr();
+
+};
+
+REGISTER_SERIALIZABLE(InteractionHashMap);
+

Modified: trunk/extra/Brefcom.cpp
===================================================================
--- trunk/extra/Brefcom.cpp	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/extra/Brefcom.cpp	2009-02-25 14:25:55 UTC (rev 1690)
@@ -13,7 +13,7 @@
 
 void BrefcomGlobalCharacteristics::compute(MetaBody* rb, bool useMaxForce){
 	//Shop::Bex::initCache();
-	#if BEX_CONTAINER
+	#ifdef BEX_CONTAINER
 		rb->bex.sync();
 	#else
 		throw runtime_error("Brefcom can run only with BexContainer");
@@ -159,9 +159,13 @@
 	/* const Real& transStrainCoeff(BC->transStrainCoeff); const Real& epsTrans(BC->epsTrans); const Real& xiShear(BC->xiShear); */
 	Real& omega(BC->omega); Real& sigmaN(BC->sigmaN);  Vector3r& sigmaT(BC->sigmaT); Real& Fn(BC->Fn); Vector3r& Fs(BC->Fs); // for python access
 
+	#define NNAN(a) assert(!isnan(a));
+	#define NNANV(v) assert(!isnan(v[0])); assert(!isnan(v[1])); assert(!isnan(v[2]));
+
 	assert(contGeom->hasShear);
 	//timingDeltas->checkpoint("setup");
 	epsN=contGeom->epsN(); epsT=contGeom->epsT();
+	NNAN(epsN); NNANV(epsT);
 	// already in SpheresContactGeometry:
 	// contGeom->relocateContactPoints(); // allow very large mutual rotations
 	if(logStrain && epsN<0){ Real epsN0=epsN; epsN=log(epsN0+1); epsT*=epsN/epsN0; }
@@ -181,8 +185,6 @@
 		LOG_DEBUG("Contact #"<<I->getId1()<<"=#"<<I->getId2()<<" is damaged over thershold ("<<omega<<">"<<omegaThreshold<<") and has been deleted (isReal="<<I->isReal<<")");
 		return;
 	}
-	#define NNAN(a) assert(!isnan(a));
-	#define NNANV(v) assert(!isnan(v[0])); assert(!isnan(v[1])); assert(!isnan(v[2]));
 	// store Fn (and Fs?), for use with GlobalStiffnessCounter?
 	NNAN(sigmaN); NNANV(sigmaT); NNAN(crossSection);
 

Modified: trunk/gui/SConscript
===================================================================
--- trunk/gui/SConscript	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/gui/SConscript	2009-02-25 14:25:55 UTC (rev 1690)
@@ -65,6 +65,7 @@
 				'InteractionPhysicsMetaEngine',
 				'PhysicalParametersMetaEngine',
 				'ConstitutiveLawDispatcher',
+				'InteractionDispatchers',
 				'STLImporter',
 				'ParallelEngine'
 			],

Modified: trunk/gui/py/timing.py
===================================================================
--- trunk/gui/py/timing.py	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/gui/py/timing.py	2009-02-25 14:25:55 UTC (rev 1690)
@@ -19,9 +19,9 @@
 	raw=[]
 	raw.append(label)
 	raw.append(str(count) if count>=0 else '')
-	raw.append((str(time/1000)+u'μs') if time>=0 else '')
+	raw.append((str(time/1000)+u'us') if time>=0 else '')
 	raw.append(('%6.2f%%'%(time*100./totalTime)) if totalTime>0 else '')
-	return ' '.join([
+	return u' '.join([
 		(sp+raw[0]).ljust(_statCols['label']),
 		(raw[1]+negSp).rjust(_statCols['count']),
 		(raw[2]+negSp).rjust(_statCols['time']),
@@ -40,7 +40,7 @@
 def _engines_stats(engines,totalTime,level):
 	lines=0; hereLines=0
 	for e in engines:
-		if e.__class__.__name__!='EngineUnit': print _formatLine('"'+e['label']+'"' if e['label'] else e.name,e.execTime,e.execCount,totalTime,level); lines+=1; hereLines+=1
+		if e.__class__.__name__!='EngineUnit': print _formatLine(u'"'+e['label']+'"' if e['label'] else e.name,e.execTime,e.execCount,totalTime,level); lines+=1; hereLines+=1
 		if e.timingDeltas: 
 			if e.__class__.__name__=='EngineUnit':
 				print _formatLine(e.name,-1,-1,-1,level); lines+=1; hereLines+=1
@@ -48,6 +48,10 @@
 			else: execTime=e.execTime
 			lines+=_delta_stats(e.timingDeltas,execTime,level+1)
 		if e.__class__.__name__=='MetaEngine': lines+=_engines_stats(e.functors,e.execTime,level+1)
+		if e.__class__.__name__=='InteractionDispatcher':
+			lines+=_engines_stats(e.geomDispatcher.functors,e.execTime,level+1)
+			lines+=_engines_stats(e.physDispatcher.functors,e.execTime,level+1)
+			lines+=_engines_stats(e.constLawDispatcher.functors,e.execTime,level+1)
 		elif e.__class__.__name__=='ParallelEngine': lines+=_engines_stats(e.slave,e.execTime,level+1)
 	if hereLines>1:
 		print _formatLine('TOTAL',totalTime,-1,totalTime,level); lines+=1

Modified: trunk/gui/py/utils.py
===================================================================
--- trunk/gui/py/utils.py	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/gui/py/utils.py	2009-02-25 14:25:55 UTC (rev 1690)
@@ -306,7 +306,7 @@
 	mainloop.run()
 	pipeline.set_state(gst.STATE_NULL); pipeline.get_state()
 
-def readParamsFromTable(tableFileLine=None,noTableOk=False,**kw):
+def readParamsFromTable(tableFileLine=None,noTableOk=False,unknownOk=False,**kw):
 	"""
 	Read parameters from a file and assign them to __builtin__ variables.
 
@@ -346,8 +346,8 @@
 		for i in range(len(names)):
 			if names[i]=='description': o.tags['description']=values[i]
 			else:
-				if names[i] not in kw.keys(): raise NameError("Parameter `%s' has no default value assigned"%names[i])
-				kw.pop(names[i])
+				if names[i] not in kw.keys() and not unknownOk: raise NameError("Parameter `%s' has no default value assigned"%names[i])
+				if names[i] in kw.keys(): kw.pop(names[i])
 				eq="%s=%s"%(names[i],values[i])
 				exec('__builtin__.%s=%s'%(names[i],values[i])); tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
 	defaults=[]

Modified: trunk/gui/py/yade-multi
===================================================================
--- trunk/gui/py/yade-multi	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/gui/py/yade-multi	2009-02-25 14:25:55 UTC (rev 1690)
@@ -106,7 +106,7 @@
 
 parser=optparse.OptionParser(usage='%prog [options] TABLE SIMULATION.py\n\n  %prog runs yade simulation multiple times with different parameters.\n  See http://yade.wikia.com/wiki/ScriptParametricStudy for details.')
 parser.add_option('-j',dest='maxJobs',type='int',help="Maximum number of simultaneous jobs to run (default: number of cores, i.e. %d)"%getNumCores(),metavar='NUM',default=getNumCores())
-parser.add_option('--log',dest='logFormat',help='Format of log files -- must contain a % or @, which will be replaced by line number or by description column respectively (default: SIMULATION.%.log)',metavar='FORMAT')
+parser.add_option('--log',dest='logFormat',help='Format of log files -- must contain a % or @, which will be replaced by line number or by description column respectively (default: SIMULATION.@.log)',metavar='FORMAT')
 parser.add_option('-l','--lines',dest='lineList',help='Lines of TABLE to use, in the format 2,3-5,8,11-13 (default: all available lines in TABLE)',metavar='LIST')
 parser.add_option('--nice',dest='nice',type='int',help='Nice value of spawned jobs (default: 10)',default=10)
 parser.add_option('--executable',dest='executable',help='Name of the program to run (default: %s)'%sys.argv[0][:-6],default=sys.argv[0][:-6],metavar='FILE') ## strip the '-multi' extension
@@ -118,14 +118,24 @@
 	parser.print_help()
 	sys.exit(1)
 table,simul=args[0:2]
-if not logFormat: logFormat=(simul[:-3] if simul[-3:]=='.py' else simul)+".%.log"
+if not logFormat: logFormat=(simul[:-3] if simul[-3:]=='.py' else simul)+".@.log"
 if (not '%' in logFormat) and ('@' not in logFormat): raise StandardError("Log string must contain at least one of `%', `@'")
 
 print "Will run `%s' on `%s' with nice value %d, output redirected to `%s', %d jobs at a time."%(executable,simul,nice,logFormat,maxJobs)
 
 ll=['']+open(table,'r').readlines()
 availableLines=[i for i in range(len(ll)) if not re.match(r'^\s*(#.*)?$',ll[i][:-1]) and i>1]
+print availableLines
 
+# read actual data
+values={}
+headings=ll[1].split()
+for l in availableLines:
+	val={}
+	for i in range(len(headings)):
+		val[i]=ll[l].split()[i]
+	values[l]=val
+
 print "Will use table `%s', with available lines"%(table),', '.join([str(i) for i in availableLines])+'.'
 
 if lineList:
@@ -145,9 +155,9 @@
 print "Will use lines ",', '.join([str(i) for i in useLines])+'.'
 # find column where description is
 try:
-	idColumn=ll[1].split().index('description')
+	idColumn=headings.index('description')
 	idStrings={}
-	for i in useLines: idStrings[i]=ll[i].split()[idColumn] # textual descripion of respective lines 
+	for i in useLines: idStrings[i]=values[i][idColumn] # textual descripion of respective lines 
 	print idStrings
 except ValueError:
 	idColumn=None
@@ -158,7 +168,11 @@
 for i,l in enumerate(useLines):
 	logFile=logFormat.replace('%',str(l))
 	if idStrings: logFile=logFile.replace('@',idStrings[l])
-	jobs.append(JobInfo(i,idStrings[l] if idStrings else '#'+str(i),'PARAM_TABLE=%s:%d nice -n %d %s -N PythonUI -- -n -x %s > %s 2>&1'%(table,l,nice,executable,simul,logFile),logFile))
+	else: logFile=logFile.replace('@',str(l))
+	envVars=[]
+	for col,head in enumerate(headings):
+		if re.match('^[A-Z_]+[A-Z0-9_]+',head): envVars+=['%s=%s'%(head,values[l][col])]
+	jobs.append(JobInfo(i,idStrings[l] if idStrings else '#'+str(i),'PARAM_TABLE=%s:%d %s nice -n %d %s -N PythonUI -- -n -x %s > %s 2>&1'%(table,l,' '.join(envVars),nice,executable,simul,logFile),logFile))
 
 print "Job summary:"
 for job in jobs:

Modified: trunk/gui/py/yadeControl.cpp
===================================================================
--- trunk/gui/py/yadeControl.cpp	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/gui/py/yadeControl.cpp	2009-02-25 14:25:55 UTC (rev 1690)
@@ -43,6 +43,7 @@
 #include<yade/pkg-common/InteractionPhysicsMetaEngine.hpp>
 #include<yade/pkg-common/PhysicalParametersMetaEngine.hpp>
 #include<yade/pkg-common/ConstitutiveLawDispatcher.hpp>
+#include<yade/pkg-common/InteractionDispatchers.hpp>
 #include<yade/pkg-common/PhysicalActionDamper.hpp>
 #include<yade/pkg-common/PhysicalActionApplier.hpp>
 #include<yade/pkg-common/MetaInteractingGeometry.hpp>
@@ -228,7 +229,6 @@
 	python::object timingDeltas_get(){return proxee->timingDeltas?python::object(pyTimingDeltas(proxee->timingDeltas)):python::object();}
 BASIC_PY_PROXY_TAIL;
 
-
 BASIC_PY_PROXY_HEAD(pyMetaEngine,MetaEngine)
 		// additional constructor
 		pyMetaEngine(string clss, python::list functors){init(clss); functors_set(functors);}
@@ -273,11 +273,24 @@
 	PY_PROXY_TIMING
 BASIC_PY_PROXY_TAIL;
 
+BASIC_PY_PROXY_HEAD(pyInteractionDispatchers,InteractionDispatchers)
+	pyInteractionDispatchers(python::list geomFunctors, python::list physFunctors, python::list constLawFunctors){
+		init("InteractionDispatchers");
+		pyMetaEngine(proxee->geomDispatcher).functors_set(geomFunctors);
+		pyMetaEngine(proxee->physDispatcher).functors_set(physFunctors);
+		pyMetaEngine(proxee->constLawDispatcher).functors_set(constLawFunctors);
+	}
+	pyMetaEngine geomDispatcher_get(void){ return pyMetaEngine(proxee->geomDispatcher);}
+	pyMetaEngine physDispatcher_get(void){ return pyMetaEngine(proxee->physDispatcher);}
+	pyMetaEngine constLawDispatcher_get(void){ return pyMetaEngine(proxee->constLawDispatcher);}
+	PY_PROXY_TIMING
+BASIC_PY_PROXY_TAIL;
+
 python::list anyEngines_get(const vector<shared_ptr<Engine> >& engContainer){
 	python::list ret; 
 	FOREACH(const shared_ptr<Engine>& eng, engContainer){
 		#define APPEND_ENGINE_IF_POSSIBLE(engineType,pyEngineType) { shared_ptr<engineType> e=dynamic_pointer_cast<engineType>(eng); if(e) { ret.append(pyEngineType(e)); continue; } }
-		APPEND_ENGINE_IF_POSSIBLE(MetaEngine,pyMetaEngine); APPEND_ENGINE_IF_POSSIBLE(StandAloneEngine,pyStandAloneEngine); APPEND_ENGINE_IF_POSSIBLE(DeusExMachina,pyDeusExMachina); APPEND_ENGINE_IF_POSSIBLE(ParallelEngine,pyParallelEngine); 
+		APPEND_ENGINE_IF_POSSIBLE(InteractionDispatchers,pyInteractionDispatchers); APPEND_ENGINE_IF_POSSIBLE(MetaEngine,pyMetaEngine); APPEND_ENGINE_IF_POSSIBLE(StandAloneEngine,pyStandAloneEngine); APPEND_ENGINE_IF_POSSIBLE(DeusExMachina,pyDeusExMachina); APPEND_ENGINE_IF_POSSIBLE(ParallelEngine,pyParallelEngine); 
 		throw std::runtime_error("Unknown engine type: `"+eng->getClassName()+"' (only MetaEngine, StandAloneEngine, DeusExMachina and ParallelEngine are supported)");
 	}
 	return ret;
@@ -289,7 +302,7 @@
 	engContainer.clear();
 	for(int i=0; i<len; i++){
 		#define PUSH_BACK_ENGINE_IF_POSSIBLE(pyEngineType) if(python::extract<pyEngineType>(PySequence_GetItem(egs.ptr(),i)).check()){ pyEngineType e=python::extract<pyEngineType>(PySequence_GetItem(egs.ptr(),i)); engContainer.push_back(e.proxee); /* cerr<<"added "<<e.pyStr()<<", a "<<#pyEngineType<<endl; */ continue; }
-		PUSH_BACK_ENGINE_IF_POSSIBLE(pyStandAloneEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyMetaEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyDeusExMachina); PUSH_BACK_ENGINE_IF_POSSIBLE(pyParallelEngine);
+		PUSH_BACK_ENGINE_IF_POSSIBLE(pyStandAloneEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyMetaEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyDeusExMachina); PUSH_BACK_ENGINE_IF_POSSIBLE(pyParallelEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyInteractionDispatchers);
 		throw std::runtime_error("Encountered unknown engine type (unable to extract from python object)");
 	}
 }
@@ -707,6 +720,12 @@
 		.def(python::init<python::list>());
 	BASIC_PY_PROXY_WRAPPER(pyDeusExMachina,"DeusExMachina")
 		TIMING_PROPS(pyDeusExMachina);
+	BASIC_PY_PROXY_WRAPPER(pyInteractionDispatchers,"InteractionDispatchers")
+		.def(python::init<python::list,python::list,python::list>())
+		.add_property("geomDispatcher",&pyInteractionDispatchers::geomDispatcher_get)
+		.add_property("physDispatcher",&pyInteractionDispatchers::physDispatcher_get)
+		.add_property("constLawDispatcher",&pyInteractionDispatchers::constLawDispatcher_get)
+		TIMING_PROPS(pyInteractionDispatchers);
 	BASIC_PY_PROXY_WRAPPER(pyEngineUnit,"EngineUnit")
 		.add_property("timingDeltas",&pyEngineUnit::timingDeltas_get)
 		.add_property("bases",&pyEngineUnit::bases_get);

Added: trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp
===================================================================
--- trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp	2009-02-25 14:25:55 UTC (rev 1690)
@@ -0,0 +1,32 @@
+#include"InteractionDispatchers.hpp"
+
+YADE_PLUGIN("InteractionDispatchers");
+
+InteractionDispatchers::InteractionDispatchers(){
+	geomDispatcher=shared_ptr<InteractionGeometryMetaEngine>(new InteractionGeometryMetaEngine);
+	physDispatcher=shared_ptr<InteractionPhysicsMetaEngine>(new InteractionPhysicsMetaEngine);
+	constLawDispatcher=shared_ptr<ConstitutiveLawDispatcher>(new ConstitutiveLawDispatcher);
+}
+
+void InteractionDispatchers::action(MetaBody* rootBody){
+	#ifdef YADE_OPENMP
+		const long size=rootBody->interactions->size();
+		#pragma omp parallel for
+		for(long i=0; i<size; i++){
+			const shared_ptr<Interaction>& I=(*rootBody->interactions)[i];
+	#else
+		FOREACH(shared_ptr<Interaction> I, *rootBody->interactions){
+	#endif
+			// InteractionGeometryMetaEngine
+			const shared_ptr<Body>& b1=Body::byId(I->getId1(),rootBody);
+			const shared_ptr<Body>& b2=Body::byId(I->getId2(),rootBody);
+			I->isReal =
+				b1->interactingGeometry && b2->interactingGeometry && // some bodies do not have interactingGeometry
+				geomDispatcher->operator()(b1->interactingGeometry, b2->interactingGeometry, b1->physicalParameters->se3, b2->physicalParameters->se3,I);
+			if(!I->isReal) continue;
+			// InteractionPhysicsMetaEngine
+			physDispatcher->operator()(b1->physicalParameters, b2->physicalParameters,I);
+			// ConstitutiveLawDispatcher
+			constLawDispatcher->operator()(I->interactionGeometry,I->interactionPhysics,I.get(),rootBody);
+		}
+}

Added: trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.hpp
===================================================================
--- trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.hpp	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.hpp	2009-02-25 14:25:55 UTC (rev 1690)
@@ -0,0 +1,22 @@
+// 2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+#pragma once
+#include<yade/core/StandAloneEngine.hpp>
+#include<yade/pkg-common/InteractionGeometryMetaEngine.hpp>
+#include<yade/pkg-common/InteractionPhysicsMetaEngine.hpp>
+#include<yade/pkg-common/ConstitutiveLawDispatcher.hpp>
+
+class InteractionDispatchers: public StandAloneEngine {
+	public:
+		InteractionDispatchers();
+		virtual void action(MetaBody*);
+		shared_ptr<InteractionGeometryMetaEngine> geomDispatcher;
+		shared_ptr<InteractionPhysicsMetaEngine> physDispatcher;
+		shared_ptr<ConstitutiveLawDispatcher> constLawDispatcher;
+		REGISTER_CLASS_AND_BASE(InteractionDispatchers,StandAloneEngine);
+		REGISTER_ATTRIBUTES(StandAloneEngine,
+			(geomDispatcher)
+			(physDispatcher)
+			(constLawDispatcher)
+		);
+};
+REGISTER_SERIALIZABLE(InteractionDispatchers);

Modified: trunk/pkg/common/SConscript
===================================================================
--- trunk/pkg/common/SConscript	2009-02-24 13:13:14 UTC (rev 1689)
+++ trunk/pkg/common/SConscript	2009-02-25 14:25:55 UTC (rev 1690)
@@ -104,6 +104,7 @@
 	env.SharedLibrary('PhysicalActionApplier',['Engine/MetaEngine/PhysicalActionApplier.cpp']),
 	env.SharedLibrary('PhysicalActionDamper',['Engine/MetaEngine/PhysicalActionDamper.cpp']),
 	env.SharedLibrary('ConstitutiveLawDispatcher',['Engine/MetaEngine/ConstitutiveLawDispatcher.cpp'],LIBS=env['LIBS']+['Force','Momentum']),
+	env.SharedLibrary('InteractionDispatchers',['Engine/MetaEngine/InteractionDispatchers.cpp'],LIBS=env['LIBS']+['InteractionGeometryMetaEngine','InteractionPhysicsMetaEngine','ConstitutiveLawDispatcher']),
 	env.SharedLibrary('InteractingBox2AABB',['Engine/EngineUnit/InteractingBox2AABB.cpp'],
 		LIBS=env['LIBS']+['BoundingVolumeMetaEngine','InteractingBox','AABB','Box',]),
 	env.SharedLibrary('MetaInteractingGeometry2AABB',['Engine/EngineUnit/MetaInteractingGeometry2AABB.cpp'],