← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 2174: 1. Clarify error message i Law2 functor is not found (thanks, Chiara)

 

------------------------------------------------------------
revno: 2174
committer: Václav Šmilauer <eudoxos@xxxxxxxx>
branch nick: trunk
timestamp: Fri 2010-04-23 13:23:15 +0200
message:
  1. Clarify error message i Law2 functor is not found (thanks, Chiara)
  2. Make static attributes initialized by values provided in YADE_CLASS_BASE_DOC_STATICATTRS
  3. documentation fixes here and there
modified:
  core/main/main.py.in
  lib/serialization/Serializable.cpp
  lib/serialization/Serializable.hpp
  pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp
  pkg/common/RenderingEngine/Gl1_Sphere.cpp
  py/_eudoxos.cpp
  py/plot.py
  py/timing.py
  py/yadeWrapper/yadeWrapper.cpp


--
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
=== modified file 'core/main/main.py.in'
--- core/main/main.py.in	2010-04-09 11:25:18 +0000
+++ core/main/main.py.in	2010-04-23 11:23:15 +0000
@@ -127,7 +127,7 @@
 			banner='[[ ^L clears screen, ^U kills line.'+(' F12 controller, F11 3d view, F10 both, F9 generator, F8 plot.' if qtEnabled else '')+' ]]',
 			rc_override=dict( # ipython options, see e.g. http://www.cv.nrao.edu/~rreid/casa/tips/ipy_user_conf.py
 				prompt_in1='Yade [\#]: ',
-				prompt_in2='     .\D.. ',
+				prompt_in2='     .\D.: ',
 				prompt_out=" ->  [\#]: ",
 				separate_in='0',
 				separate_out='0',

=== modified file 'lib/serialization/Serializable.cpp'
--- lib/serialization/Serializable.cpp	2010-03-28 21:22:25 +0000
+++ lib/serialization/Serializable.cpp	2010-04-23 11:23:15 +0000
@@ -11,7 +11,7 @@
 #include "Serializable.hpp"
 
 
-void Serializable::pyRegisterClass(boost::python::object _scope) const {
+void Serializable::pyRegisterClass(boost::python::object _scope) {
 	if(!checkPyClassRegistersItself("Serializable")) return;
 	boost::python::scope thisScope(_scope); 
 	python::class_<Serializable, shared_ptr<Serializable>, noncopyable >("Serializable")

=== modified file 'lib/serialization/Serializable.hpp'
--- lib/serialization/Serializable.hpp	2010-04-20 08:55:02 +0000
+++ lib/serialization/Serializable.hpp	2010-04-23 11:23:15 +0000
@@ -135,8 +135,11 @@
 	template<> struct py_wrap_ref<Matrix3r>: public boost::true_type{};
 };
 #define _DEF_READWRITE_BY_VALUE(thisClass,attr,doc) add_property(/*attr name*/BOOST_PP_STRINGIZE(attr),/*read access*/boost::python::make_getter(&thisClass::attr,boost::python::return_value_policy<boost::python::return_by_value>()),/*write access*/boost::python::make_setter(&thisClass::attr,boost::python::return_value_policy<boost::python::return_by_value>()),/*docstring*/doc)
+/* Huh, add_static_property does not support doc argument (add_property does); if so, use add_property for now at least... */
+#define _DEF_READWRITE_BY_VALUE_STATIC(thisClass,attr,doc)  _DEF_READWRITE_BY_VALUE(thisClass,attr,doc)
 // the conditional should be eliminated by compiler at compile-time, as it depends only on types, not their values
 #define _DEF_READWRITE_CUSTOM(thisClass,attr,doc) { if(yade::py_wrap_ref<typeof(thisClass::attr)>::value) _classObj.def_readwrite(BOOST_PP_STRINGIZE(attr),&thisClass::attr,doc); else _classObj._DEF_READWRITE_BY_VALUE(thisClass,attr,doc); }
+#define _DEF_READWRITE_CUSTOM_STATIC(thisClass,attr,doc) { if(yade::py_wrap_ref<typeof(thisClass::attr)>::value) _classObj.def_readwrite(BOOST_PP_STRINGIZE(attr),&thisClass::attr,doc); else _classObj._DEF_READWRITE_BY_VALUE_STATIC(thisClass,attr,doc); }
 
 // macros for deprecated attribute access
 // gcc<=4.3 is not able to compile this code; we will just not generate any code for deprecated attributes in such case
@@ -173,7 +176,7 @@
 	REGISTER_ATTRIBUTES_DEPREC(thisClass,baseClass,BOOST_PP_SEQ_FOR_EACH(_STRIPDOC2,thisClass,attrs),deprec) \
 	REGISTER_CLASS_AND_BASE(thisClass,baseClass) \
 	/* accessors for deprecated attributes, with warnings */ BOOST_PP_SEQ_FOR_EACH(_ACCESS_DEPREC,thisClass,deprec) \
-	/* python class registration */ virtual void pyRegisterClass(python::object _scope) const { if(!checkPyClassRegistersItself(#thisClass)) return; boost::python::scope thisScope(_scope); YADE_SET_DOCSTRING_OPTS; boost::python::class_<thisClass,shared_ptr<thisClass>,boost::python::bases<baseClass>,boost::noncopyable> _classObj(#thisClass,docString); _classObj.def("__init__",python::raw_constructor(Serializable_ctor_kwAttrs<thisClass>)).def("clone",&Serializable_clone<thisClass>,python::arg("attrs")=python::dict()); BOOST_PP_SEQ_FOR_EACH(_PYATTR_DEF,thisClass,attrs); (void) _classObj BOOST_PP_SEQ_FOR_EACH(_PYATTR_DEPREC_DEF,thisClass,deprec); (void) _classObj extras ; }
+	/* python class registration */ virtual void pyRegisterClass(python::object _scope) { if(!checkPyClassRegistersItself(#thisClass)) return; boost::python::scope thisScope(_scope); YADE_SET_DOCSTRING_OPTS; boost::python::class_<thisClass,shared_ptr<thisClass>,boost::python::bases<baseClass>,boost::noncopyable> _classObj(#thisClass,docString); _classObj.def("__init__",python::raw_constructor(Serializable_ctor_kwAttrs<thisClass>)).def("clone",&Serializable_clone<thisClass>,python::arg("attrs")=python::dict()); BOOST_PP_SEQ_FOR_EACH(_PYATTR_DEF,thisClass,attrs); (void) _classObj BOOST_PP_SEQ_FOR_EACH(_PYATTR_DEPREC_DEF,thisClass,deprec); (void) _classObj extras ; }
 	// use later: void must_use_both_YADE_CLASS_BASE_DOC_ATTRS_and_YADE_PLUGIN(); 
 // #define YADE_CLASS_BASE_DOC_ATTRS_PY(thisClass,baseClass,docString,attrs,extras) YADE_CLASS_BASE_DOC_ATTRS_DEPREC_PY(thisClass,baseClass,docString,attrs,,extras)
 
@@ -193,16 +196,19 @@
 	_YADE_CLASS_BASE_DOC_ATTRS_DEPREC_PY(thisClass,baseClass,docString,BOOST_PP_SEQ_FOR_EACH(_STRIPDECL4,~,attrDecls),deprec,extras)
 
 #define _DEF_READWRITE_STATIC(thisClass,attr,doc)
-#define _STATATTR_PY(x,thisClass,z) _DEF_READWRITE_CUSTOM(thisClass,BOOST_PP_TUPLE_ELEM(4,1,z),/*docstring*/ "|ystatic| :ydefault:`" BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(4,2,z)) "` " BOOST_PP_TUPLE_ELEM(4,3,z))
+#define _STATATTR_PY(x,thisClass,z) _DEF_READWRITE_CUSTOM_STATIC(thisClass,BOOST_PP_TUPLE_ELEM(4,1,z),/*docstring*/ "|ystatic| :ydefault:`" BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(4,2,z)) "` " BOOST_PP_TUPLE_ELEM(4,3,z))
 #define _STATATTR_DECL(x,y,z) static BOOST_PP_TUPLE_ELEM(4,0,z) BOOST_PP_TUPLE_ELEM(4,1,z);
 #define _STRIP_TYPE_DEFAULT_DOC(x,y,z) (BOOST_PP_TUPLE_ELEM(4,1,z))
+#define _STATATTR_SET(x,thisClass,z) thisClass::BOOST_PP_TUPLE_ELEM(4,1,z)=BOOST_PP_TUPLE_ELEM(4,2,z);
 
 #define YADE_CLASS_BASE_DOC_STATICATTRS(thisClass,baseClass,docString,attrs)\
 	public: BOOST_PP_SEQ_FOR_EACH(_STATATTR_DECL,~,attrs) /* attribute declarations */ \
 	/* no ctor */ \
 	REGISTER_CLASS_AND_BASE(thisClass,baseClass); \
 	REGISTER_ATTRIBUTES(baseClass,BOOST_PP_SEQ_FOR_EACH(_STRIP_TYPE_DEFAULT_DOC,~,attrs)) \
-	virtual void pyRegisterClass(python::object _scope) const { if(!checkPyClassRegistersItself(#thisClass)) return; boost::python::scope thisScope(_scope); YADE_SET_DOCSTRING_OPTS; \
+	/* called only at class registration, to set initial values; storage still has to be alocated in the cpp file! */ \
+	void initSetStaticAttributesValue(void){ BOOST_PP_SEQ_FOR_EACH(_STATATTR_SET,thisClass,attrs); } \
+	virtual void pyRegisterClass(python::object _scope) { if(!checkPyClassRegistersItself(#thisClass)) return; initSetStaticAttributesValue(); boost::python::scope thisScope(_scope); YADE_SET_DOCSTRING_OPTS; \
 		boost::python::class_<thisClass,shared_ptr<thisClass>,boost::python::bases<baseClass>,boost::noncopyable> _classObj(#thisClass,docString); \
 		BOOST_PP_SEQ_FOR_EACH(_STATATTR_PY,thisClass,attrs);  \
 	}
@@ -341,7 +347,7 @@
 		// that means that the class doesn't register itself properly
 		virtual bool checkPyClassRegistersItself(const std::string& thisClassName) const;
 		// perform class registration; overridden in all classes
-		virtual void pyRegisterClass(boost::python::object _scope) const;
+		virtual void pyRegisterClass(boost::python::object _scope);
 		
 		//! update attributes from dictionary
 		void pyUpdateAttrs(const boost::python::dict& d);

=== modified file 'pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp'
--- pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp	2010-04-09 11:25:18 +0000
+++ pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp	2010-04-23 11:23:15 +0000
@@ -139,7 +139,7 @@
 			if(!I->functorCache.constLaw){
 				I->functorCache.constLaw=lawDispatcher->getFunctor2D(I->interactionGeometry,I->interactionPhysics,swap);
 				if(!I->functorCache.constLaw){
-					LOG_FATAL("getFunctor2D returned empty functor for  #"<<I->getId1()<<"+"<<I->getId2()<<", types "<<I->interactionGeometry->getClassName()<<"="<<I->interactionGeometry->getClassIndex()<<" and "<<I->interactionPhysics->getClassName()<<"="<<I->interactionPhysics->getClassIndex());
+					LOG_FATAL("None of given Law2 functors can handle interaction #"<<I->getId1()<<"+"<<I->getId2()<<", types geom:"<<I->interactionGeometry->getClassName()<<"="<<I->interactionGeometry->getClassIndex()<<" and phys:"<<I->interactionPhysics->getClassName()<<"="<<I->interactionPhysics->getClassIndex()<<" (LawDispatcher::getFunctor2D returned empty functor)");
 					//abort();
 					exit(1);
 				}

=== modified file 'pkg/common/RenderingEngine/Gl1_Sphere.cpp'
--- pkg/common/RenderingEngine/Gl1_Sphere.cpp	2010-03-27 22:18:10 +0000
+++ pkg/common/RenderingEngine/Gl1_Sphere.cpp	2010-04-23 11:23:15 +0000
@@ -10,11 +10,11 @@
 #include<yade/pkg-common/Sphere.hpp>
 #include<yade/lib-opengl/OpenGLWrapper.hpp>
 
-bool Gl1_Sphere::wire=false;
-bool Gl1_Sphere::stripes=false;
-bool Gl1_Sphere::glutNormalize=true;
-int  Gl1_Sphere::glutSlices=12;
-int  Gl1_Sphere::glutStacks=6;
+bool Gl1_Sphere::wire;
+bool Gl1_Sphere::stripes;
+bool Gl1_Sphere::glutNormalize;
+int  Gl1_Sphere::glutSlices;
+int  Gl1_Sphere::glutStacks;
 
 vector<Vector3r> Gl1_Sphere::vertices, Gl1_Sphere::faces;
 int Gl1_Sphere::glSphereList=-1;

=== modified file 'py/_eudoxos.cpp'
--- py/_eudoxos.cpp	2010-04-18 20:46:54 +0000
+++ py/_eudoxos.cpp	2010-04-23 11:23:15 +0000
@@ -252,7 +252,7 @@
 	// cleanup
 	~InteractionLocator(){ locator->Delete(); points->Delete(); grid->Delete(); }
 
-	py::list intrsWithinDistance(const Vector3r& pt, Real radius){
+	py::list intrsAroundPt(const Vector3r& pt, Real radius){
 		vtkIdList *ids=vtkIdList::New();
 		locator->FindPointsWithinRadius(radius,(const double*)(&pt),ids);
 		int numIds=ids->GetNumberOfIds();
@@ -263,6 +263,29 @@
 		}
 		return ret;
 	}
+	python::tuple macroAroundPt(const Vector3r& pt, Real radius){
+		Matrix3r ss(Matrix3r::ZERO);
+		vtkIdList *ids=vtkIdList::New();
+		locator->FindPointsWithinRadius(radius,(const double*)(&pt),ids);
+		int numIds=ids->GetNumberOfIds();
+		Real omegaCumm=0, kappaCumm=0;
+		for(int k=0; k<numIds; k++){
+			const shared_ptr<Interaction>& I(intrs[ids->GetId(k)]);
+			Dem3DofGeom* geom=YADE_CAST<Dem3DofGeom*>(I->interactionGeometry.get());
+			CpmPhys* phys=YADE_CAST<CpmPhys*>(I->interactionPhysics.get());
+			Real d=(geom->se31.position-geom->se32.position).Length(); // current contact length
+			const Vector3r& n=geom->normal;
+			const Real& A=phys->crossSection;
+			const Vector3r& sigmaT=phys->sigmaT;
+			const Real& sigmaN=phys->sigmaN;
+			for(int i=0; i<3; i++) for(int j=0;j<3; j++){
+				ss[i][j]+=d*A*(sigmaN*n[i]*n[j]+.5*(sigmaT[i]*n[j]+sigmaT[j]*n[i]));
+			}
+			omegaCumm+=phys->omega; kappaCumm+=phys->kappaD;
+		}
+		ss*=1/((4/3.)*Mathr::PI*pow(radius,3));
+		return py::make_tuple(ss,omegaCumm/numIds,kappaCumm/numIds);
+	}
 	py::tuple getBounds(){ return py::make_tuple(mn,mx);}
 	int getCnt(){ return cnt; }
 };
@@ -277,7 +300,8 @@
 	py::def("testNumpy",testNumpy);
 #ifdef YADE_VTK
 	py::class_<InteractionLocator>("InteractionLocator","Locate all (real) interactions in space by their :yref:`contact point<Dem3DofGeom::contactPoint>`. When constructed, all real interactions are spatially indexed (uses vtkPointLocator internally). Use intrsWithinDistance to use those data. \n\n.. note::\n\tData might become inconsistent with real simulation state if simulation is being run between creation of this object and spatial queries.")
-		.def("intrsWithinDistance",&InteractionLocator::intrsWithinDistance,((python::arg("point"),python::arg("maxDist"))),"Return list of real interactions that are not further than *maxDist* from *point*.")
+		.def("intrsAroundPt",&InteractionLocator::intrsAroundPt,((python::arg("point"),python::arg("maxDist"))),"Return list of real interactions that are not further than *maxDist* from *point*.")
+		.def("macroAroundPt",&InteractionLocator::macroAroundPt,((python::arg("point"),python::arg("maxDist"))),"Return tuple of averaged stress tensor (as Matrix3), average omega and average kappa values.")
 		.add_property("bounds",&InteractionLocator::getBounds,"Return coordinates of lower and uppoer corner of axis-aligned abounding box of all interactions")
 		.add_property("count",&InteractionLocator::getCnt,"Number of interactions held")
 	;
@@ -287,5 +311,5 @@
 		python::init<Real,int,Real>((python::arg("dH_dTheta"),python::arg("axis")=0,python::arg("theta0")=0),":Parameters:\n\n\tdH_dTheta: float\n\t\tSpiral inclination, i.e. height increase per 1 radian turn;\n\taxis: int\n\t\tAxis of rotation (0=x,1=y,2=z)\n\ttheta: float\n\t\tSpiral angle at zero height (theta intercept)\n\n")
 	)
 		.def("intrsAroundPt",&SpiralInteractionLocator2d::intrsAroundPt,(python::arg("pt2d"),python::arg("radius")),"Return list of interaction objects that are not further from *pt2d* than *radius* in the projection plane")
-		.def("macroStressAroundPt",&SpiralInteractionLocator2d::macroStressAroundPt,(python::arg("pt2d"),python::arg("radius")),"Compute macroscopic stress around given point, rotating the interaction to the projection plane first. The formula used is\n\n.. math::\n\n    \\sigma_{ij}=\\frac{1}{V}\\sum_{IJ}d^{IJ}A^{IJ}\\left[\\sigma^{N,IJ}n_i^{IJ}n_j^{IJ}+\\frac{1}{2}\\left(\\sigma_i^{T,IJ}n_j^{IJ}+\\sigma_j^{T,IJ}n_i^{IJ}\\right)\\right]\n\nwhere the sum is taken over volume $V$ containing interactions $IJ$ between spheres $I$ and $J$;\n* $i$, $j$ indices denote Cartesian components of vectors and tensors,\n* $d^{IJ}$ is current distance between spheres $I$ and $J$,\n* $A^{IJ}$ is area of contact $IJ$,\n* $n$ is interaction normal (unit vector pointing from center of $I$ to the center of $J$)\n* $\\sigma^{N,IJ}$  is normal stress (as scalar) in contact $IJ$,\n* $\\sigma^{T,IJ}$ is shear stress in contact $IJ$ in global coordinates.\n\n$\\sigma^{T}$ and $n$ are transformed by angle $\\theta$ as given by :yref:`utils.spiralProject`.");
+		.def("macroStressAroundPt",&SpiralInteractionLocator2d::macroStressAroundPt,(python::arg("pt2d"),python::arg("radius")),"Compute macroscopic stress around given point, rotating the interaction to the projection plane first. The formula used is\n\n.. math::\n\n    \\sigma_{ij}=\\frac{1}{V}\\sum_{IJ}d^{IJ}A^{IJ}\\left[\\sigma^{N,IJ}n_i^{IJ}n_j^{IJ}+\\frac{1}{2}\\left(\\sigma_i^{T,IJ}n_j^{IJ}+\\sigma_j^{T,IJ}n_i^{IJ}\\right)\\right]\n\nwhere the sum is taken over volume $V$ containing interactions $IJ$ between spheres $I$ and $J$;\n\n* $i$, $j$ indices denote Cartesian components of vectors and tensors,\n* $d^{IJ}$ is current distance between spheres $I$ and $J$,\n* $A^{IJ}$ is area of contact $IJ$,\n* $n$ is interaction normal (unit vector pointing from center of $I$ to the center of $J$)\n* $\\sigma^{N,IJ}$  is normal stress (as scalar) in contact $IJ$,\n* $\\sigma^{T,IJ}$ is shear stress in contact $IJ$ in global coordinates.\n\n$\\sigma^{T}$ and $n$ are transformed by angle $\\theta$ as given by :yref:`yade.utils.spiralProject`.");
 }

=== modified file 'py/plot.py'
--- py/plot.py	2010-04-10 15:11:48 +0000
+++ py/plot.py	2010-04-23 11:23:15 +0000
@@ -1,9 +1,7 @@
 # encoding: utf-8
 # 2008 © Václav Šmilauer <eudoxos@xxxxxxxx> 
 """
-Module containing utility functions for plotting inside yade.
-
-Experimental, interface may change (even drastically).
+Module containing utility functions for plotting inside yade. See :ysrc:`scripts/simple-scene-plot.py` or :ysrc:`examples/concrete/uniax.py` for example of usage.
 
 """
 import matplotlib
@@ -13,25 +11,24 @@
 matplotlib.rc('axes',grid=True) # put grid in all figures
 import pylab
 
-
-data={} # global, common for all plots: {'name':[value,...],...}
+"""Global dictionary containing all data values, common for all plots, in the form {'name':[value,...],...}. Data should be added using plot.addData function. All [value,...] columns have the same length, they are padded with NaN if unspecified."""
+# dictionary x-name -> (yspec,...), where yspec is either y-name or (y-name,'line-specification')
 plots={} # dictionary x-name -> (yspec,...), where yspec is either y-name or (y-name,'line-specification')
 "Dictionary converting names in data to human-readable names (TeX names, for instance); if a variable is not specified, it is left untranslated."
 labels={}
 #plotLines={} # dictionary x-name -> Line2d objects (that hopefully still correspond to yspec in plots)
 
 def reset():
+	"Reset all plot-related variables (data, plots, labels)"
 	global data, plots, labels # plotLines
 	data={}; plots={}; # plotLines={};
 	pylab.close('all')
 
 def resetData():
+	"Reset all plot data; keep plots and labels intact."
 	global data
 	data={}
 
-# we could have a yplot class, that would hold: (yspec,...), (Line2d,Line2d,...) ?
-
-plotDataCollector=None
 from yade.wrapper import *
 
 def splitData():

=== modified file 'py/timing.py'
--- py/timing.py	2010-03-25 13:24:10 +0000
+++ py/timing.py	2010-04-23 11:23:15 +0000
@@ -73,25 +73,25 @@
 
 	.. code-block:: none
 
-		Name                                                    Count                 Time            Rel. time
-		-------------------------------------------------------------------------------------------------------
-		ForceResetter                      400               9449μs                0.01%      
-		BoundingVolumeMetaEngine                            400            1171770μs                1.15%      
-		PersistentSAPCollider                               400            9433093μs                9.24%      
-		InteractionGeometryMetaEngine                       400           15177607μs               14.87%      
-		InteractionPhysicsMetaEngine                        400            9518738μs                9.33%      
-		ConstitutiveLawDispatcher                           400           64810867μs               63.49%      
-		  ef2_Spheres_Brefcom_BrefcomLaw                                                                       
-			 setup                                           4926145            7649131μs               15.25%  
-			 geom                                            4926145           23216292μs               46.28%  
-			 material                                        4926145            8595686μs               17.14%  
-			 rest                                            4926145           10700007μs               21.33%  
-			 TOTAL                                                             50161117μs              100.00%  
-		"damper"                                            400            1866816μs                1.83%      
-		"strainer"                                          400              21589μs                0.02%      
-		"plotDataCollector"                                 160              64284μs                0.06%      
-		"damageChecker"                                       9               3272μs                0.00%      
-		TOTAL                                                            102077490μs              100.00%      
+		Name                                     Count                 Time        Rel. time
+		------------------------------------------------------------------------------------
+		ForceResetter                        400               9449μs            0.01%      
+		BoundingVolumeMetaEngine             400            1171770μs            1.15%      
+		PersistentSAPCollider                400            9433093μs            9.24%      
+		InteractionGeometryMetaEngine        400           15177607μs           14.87%      
+		InteractionPhysicsMetaEngine         400            9518738μs            9.33%      
+		ConstitutiveLawDispatcher            400           64810867μs           63.49%      
+		  ef2_Spheres_Brefcom_BrefcomLaw                                                    
+			 setup                            4926145            7649131μs           15.25%  
+			 geom                             4926145           23216292μs           46.28%  
+			 material                         4926145            8595686μs           17.14%  
+			 rest                             4926145           10700007μs           21.33%  
+			 TOTAL                                              50161117μs          100.00%  
+		"damper"                             400            1866816μs            1.83%      
+		"strainer"                           400              21589μs            0.02%      
+		"plotDataCollector"                  160              64284μs            0.06%      
+		"damageChecker"                        9               3272μs            0.00%      
+		TOTAL                                             102077490μs          100.00%      
 
 	"""
 	print 'Name'.ljust(_statCols['label'])+' '+'Count'.rjust(_statCols['count'])+' '+'Time'.rjust(_statCols['time'])+' '+'Rel. time'.rjust(_statCols['relTime'])

=== modified file 'py/yadeWrapper/yadeWrapper.cpp'
--- py/yadeWrapper/yadeWrapper.cpp	2010-04-19 10:18:42 +0000
+++ py/yadeWrapper/yadeWrapper.cpp	2010-04-23 11:23:15 +0000
@@ -200,6 +200,7 @@
 		long countReal(){ long ret=0; FOREACH(const shared_ptr<Interaction>& I, *proxee){ if(I->isReal()) ret++; } return ret; }
 		bool serializeSorted_get(){return proxee->serializeSorted;}
 		void serializeSorted_set(bool ss){proxee->serializeSorted=ss;}
+		void eraseNonReal(){ proxee->eraseNonReal(); }
 };
 
 class pyForceContainer{
@@ -584,6 +585,7 @@
 		.def("nth",&pyInteractionContainer::pyNth,"Return n-th interaction from the container (usable for picking random interaction).")
 		.def("withBody",&pyInteractionContainer::withBody,"Return list of real interactions of given body.")
 		.def("withBodyAll",&pyInteractionContainer::withBodyAll,"Return list of all (real as well as non-real) interactions of given body.")
+		.def("eraseNonReal",&pyInteractionContainer::eraseNonReal,"Erase all interactions that are not :yref:`real <InteractionContainer.isReal>`.")
 		.add_property("serializeSorted",&pyInteractionContainer::serializeSorted_get,&pyInteractionContainer::serializeSorted_set)
 		.def("clear",&pyInteractionContainer::clear,"Remove all interactions");
 	python::class_<pyInteractionIterator>("InteractionIterator",python::init<pyInteractionIterator&>())