← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 2601: 1. Make the i/o regression test quiet by adding quiet flag to saveTmp/loadTmp

 

------------------------------------------------------------
revno: 2601
committer: Václav Šmilauer <eu@xxxxxxxx>
branch nick: yade
timestamp: Thu 2010-12-09 13:38:42 +0100
message:
  1. Make the i/o regression test quiet by adding quiet flag to saveTmp/loadTmp
  2. Facet has NaN vertices by default, and Facet.postLoad returns without warning in such case
  3. LawTester now has most data as Vector6r instead of 2xVector3r. The syntax changed (backwards-compat with warnings, sorry), path should be disPath, ptOurs, ptGeom, rotOurs, rotGeom is now in uGeom and uTest
  4. Add Vector6.head() and Vector6.tail() to return first/second triplet as Vector3 in python (corresponds to start<3>() and end<3>() in eigen2 and head<3>()/tail<3>() in eigen3)
  5. Make the qt4 interface display Vector6 properly
modified:
  core/Omega.cpp
  core/Omega.hpp
  gui/qt4/SerializableEditor.py
  pkg/common/Facet.cpp
  pkg/common/Facet.hpp
  pkg/dem/DomainLimiter.cpp
  pkg/dem/DomainLimiter.hpp
  py/mathWrap/miniEigen.cpp
  py/tests/omega.py
  py/wrapper/customConverters.cpp
  py/wrapper/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/Omega.cpp'
--- core/Omega.cpp	2010-11-07 11:46:20 +0000
+++ core/Omega.cpp	2010-12-09 12:38:42 +0000
@@ -214,12 +214,12 @@
 	buildDynlibDatabase(vector<string>(plugins.begin(),plugins.end()));
 }
 
-void Omega::loadSimulation(const string& f){
+void Omega::loadSimulation(const string& f, bool quiet){
 	bool isMem=algorithm::starts_with(f,":memory:");
 	if(!isMem && !filesystem::exists(f)) throw runtime_error("Simulation file to load doesn't exist: "+f);
 	if(isMem && memSavedSimulations.count(f)==0) throw runtime_error("Cannot load nonexistent memory-saved simulation "+f);
 	
-	LOG_INFO("Loading file "+f);
+	if(!quiet) LOG_INFO("Loading file "+f);
 	{
 		stop(); // stop current simulation if running
 		resetScene();
@@ -234,16 +234,16 @@
 	if(scene->getClassName()!="Scene") throw logic_error("Wrong file format (scene is not a Scene!?) in "+f);
 	sceneFile=f;
 	timeInit();
-	LOG_DEBUG("Simulation loaded");
+	if(!quiet) LOG_DEBUG("Simulation loaded");
 }
 
 
 
-void Omega::saveSimulation(const string& f){
+void Omega::saveSimulation(const string& f, bool quiet){
 	if(f.size()==0) throw runtime_error("f of file to save has zero length.");
-	LOG_INFO("Saving file " << f);
+	if(!quiet) LOG_INFO("Saving file " << f);
 	if(algorithm::starts_with(f,":memory:")){
-		if(memSavedSimulations.count(f)>0) LOG_INFO("Overwriting in-memory saved simulation "<<f);
+		if(memSavedSimulations.count(f)>0 && !quiet) LOG_INFO("Overwriting in-memory saved simulation "<<f);
 		ostringstream oss;
 		yade::ObjectIO::save<typeof(scene),boost::archive::binary_oarchive>(oss,"scene",scene);
 		memSavedSimulations[f]=oss.str();

=== modified file 'core/Omega.hpp'
--- core/Omega.hpp	2010-11-07 11:46:20 +0000
+++ core/Omega.hpp	2010-12-09 12:38:42 +0000
@@ -105,8 +105,8 @@
 		void stop(); // resets the simulationLoop
 		bool isRunning();
 		std::string sceneFile; // updated at load/save automatically
-		void loadSimulation(const string& name);
-		void saveSimulation(const string& name);
+		void loadSimulation(const string& name, bool quiet=false);
+		void saveSimulation(const string& name, bool quiet=false);
 
 		void resetScene();
 		const shared_ptr<Scene>& getScene();

=== modified file 'gui/qt4/SerializableEditor.py'
--- gui/qt4/SerializableEditor.py	2010-12-01 11:09:53 +0000
+++ gui/qt4/SerializableEditor.py	2010-12-09 12:38:42 +0000
@@ -232,6 +232,9 @@
 		self.trySetter(val)
 	def setFocus(self): self.grid.itemAtPosition(0,0).widget().setFocus()
 
+class AttrEditor_Vector6i(AttrEditor_MatrixXi):
+	def __init__(self,parent,getter,setter):
+		AttrEditor_MatrixXi.__init__(self,parent,getter,setter,1,6,lambda r,c:c)
 class AttrEditor_Vector3i(AttrEditor_MatrixXi):
 	def __init__(self,parent,getter,setter):
 		AttrEditor_MatrixXi.__init__(self,parent,getter,setter,1,3,lambda r,c:c)
@@ -239,6 +242,9 @@
 	def __init__(self,parent,getter,setter):
 		AttrEditor_MatrixXi.__init__(self,parent,getter,setter,1,2,lambda r,c:c)
 
+class AttrEditor_Vector6(AttrEditor_MatrixX):
+	def __init__(self,parent,getter,setter):
+		AttrEditor_MatrixX.__init__(self,parent,getter,setter,1,6,lambda r,c:c)
 class AttrEditor_Vector3(AttrEditor_MatrixX):
 	def __init__(self,parent,getter,setter):
 		AttrEditor_MatrixX.__init__(self,parent,getter,setter,1,3,lambda r,c:c)
@@ -251,8 +257,8 @@
 
 class Se3FakeType: pass
 
-_fundamentalEditorMap={bool:AttrEditor_Bool,str:AttrEditor_Str,int:AttrEditor_Int,float:AttrEditor_Float,Quaternion:AttrEditor_Quaternion,Vector2:AttrEditor_Vector2,Vector3:AttrEditor_Vector3,Matrix3:AttrEditor_Matrix3,Vector3i:AttrEditor_Vector3i,Vector2i:AttrEditor_Vector2i,Se3FakeType:AttrEditor_Se3}
-_fundamentalInitValues={bool:True,str:'',int:0,float:0.0,Quaternion:Quaternion.Identity,Vector3:Vector3.Zero,Matrix3:Matrix3.Zero,Vector3i:Vector3i.Zero,Vector2i:Vector2i.Zero,Vector2:Vector2.Zero,Se3FakeType:(Vector3.Zero,Quaternion.Identity)}
+_fundamentalEditorMap={bool:AttrEditor_Bool,str:AttrEditor_Str,int:AttrEditor_Int,float:AttrEditor_Float,Quaternion:AttrEditor_Quaternion,Vector2:AttrEditor_Vector2,Vector3:AttrEditor_Vector3,Vector6:AttrEditor_Vector6,Matrix3:AttrEditor_Matrix3,Vector6i:AttrEditor_Vector6i,Vector3i:AttrEditor_Vector3i,Vector2i:AttrEditor_Vector2i,Se3FakeType:AttrEditor_Se3}
+_fundamentalInitValues={bool:True,str:'',int:0,float:0.0,Quaternion:Quaternion.Identity,Vector3:Vector3.Zero,Matrix3:Matrix3.Zero,Vector6:Vector6.Zero,Vector6i:Vector6i.Zero,Vector3i:Vector3i.Zero,Vector2i:Vector2i.Zero,Vector2:Vector2.Zero,Se3FakeType:(Vector3.Zero,Quaternion.Identity)}
 
 class SerializableEditor(QFrame):
 	"Class displaying and modifying serializable attributes of a yade object."
@@ -298,6 +304,7 @@
 		vecMap={
 			'bool':bool,'int':int,'long':int,'Body::id_t':long,'size_t':long,
 			'Real':float,'float':float,'double':float,
+			'Vector6r':Vector6,'Vector6i':Vector6i,'Vector3i':Vector3i,'Vector2r':Vector2,'Vector2i':Vector2i,
 			'Vector3r':Vector3,'Matrix3r':Matrix3,'Se3r':Se3FakeType,
 			'string':str,
 			'BodyCallback':BodyCallback,'IntrCallback':IntrCallback,'BoundFunctor':BoundFunctor,'IGeomFunctor':IGeomFunctor,'IPhysFunctor':IPhysFunctor,'LawFunctor':LawFunctor,

=== modified file 'pkg/common/Facet.cpp'
--- pkg/common/Facet.cpp	2010-10-13 16:23:08 +0000
+++ pkg/common/Facet.cpp	2010-12-09 12:38:42 +0000
@@ -20,6 +20,7 @@
 	// in the future, a fixed-size array should be used instead of vector<Vector3r> for vertices
 	// this is prevented by yade::serialization now IIRC
 	if(vertices.size()!=3){ throw runtime_error(("Facet must have exactly 3 vertices (not "+lexical_cast<string>(vertices.size())+")").c_str()); }
+	if(isnan(vertices[0][0])) return;  // not initialized, nothing to do
 	Vector3r e[3] = {vertices[1]-vertices[0] ,vertices[2]-vertices[1] ,vertices[0]-vertices[2]};
 	#define CHECK_EDGE(i) if(e[i].squaredNorm()==0){LOG_FATAL("Facet has coincident vertices "<<i<<" ("<<vertices[i]<<") and "<<(i+1)%3<<" ("<<vertices[(i+1)%3]<<")!");}
 		CHECK_EDGE(0); CHECK_EDGE(1);CHECK_EDGE(2);

=== modified file 'pkg/common/Facet.hpp'
--- pkg/common/Facet.hpp	2010-10-13 16:23:08 +0000
+++ pkg/common/Facet.hpp	2010-12-09 12:38:42 +0000
@@ -13,7 +13,7 @@
 
 // define this to have topology information about facets enabled;
 // it is necessary for FacetTopologyAnalyzer
-#define FACET_TOPO
+// #define FACET_TOPO
 
 class Facet : public Shape {
     public:
@@ -36,7 +36,7 @@
 	void postLoad(Facet&);
 
 	YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Facet,Shape,"Facet (triangular particle) geometry.",
-		((vector<Vector3r>,vertices,vector<Vector3r>(3),(Attr::triggerPostLoad | Attr::noResize),"Vertex positions in local coordinates."))
+		((vector<Vector3r>,vertices,vector<Vector3r>(3,Vector3r(NaN,NaN,NaN)),(Attr::triggerPostLoad | Attr::noResize),"Vertex positions in local coordinates."))
 		#ifdef FACET_TOPO
 		((vector<Body::id_t>,edgeAdjIds,vector<Body::id_t>(3,Body::ID_NONE),,"Facet id's that are adjacent to respective edges [experimental]"))
 		((vector<Real>,edgeAdjHalfAngle,vector<Real>(3,0),,"half angle between normals of this facet and the adjacent facet [experimental]"))

=== modified file 'pkg/dem/DomainLimiter.cpp'
--- pkg/dem/DomainLimiter.cpp	2010-12-06 22:58:31 +0000
+++ pkg/dem/DomainLimiter.cpp	2010-12-09 12:38:42 +0000
@@ -32,15 +32,17 @@
 void LawTester::postLoad(LawTester&){
 	if(ids.size()==0) return; // uninitialized object, don't do nothing at all
 	if(ids.size()!=2) throw std::invalid_argument("LawTester.ids: exactly two values must be given.");
-	if(path.empty() && rotPath.empty()) throw invalid_argument("LawTester.{path,rotPath}: at least one point must be given.");
+	if(disPath.empty() && rotPath.empty()) throw invalid_argument("LawTester.{disPath,rotPath}: at least one point must be given.");
 	if(pathSteps.empty()) throw invalid_argument("LawTester.pathSteps: at least one value must be given.");
-	size_t pathSize=max(path.size(),rotPath.size());
+	size_t pathSize=max(disPath.size(),rotPath.size());
 	// update path points
-	_pathU.clear(); _pathU.push_back(Vector3r::Zero());
-	_pathR.clear(); _pathR.push_back(Vector3r::Zero());
+	_path.clear(); _path.push_back(Vector6r::Zero());
 	for(size_t i=0; i<pathSize; i++) {
-		_pathU.push_back(i<path.size()?path[i]:(path.empty()?Vector3r::Zero():*(path.rbegin())));
-		_pathR.push_back(i<rotPath.size()?rotPath[i]:(rotPath.empty()?Vector3r::Zero():*(rotPath.rbegin())));
+		Vector6r pt;
+		pt.start<3>()=Vector3r(i<disPath.size()?disPath[i]:(disPath.empty()?Vector3r::Zero():*(disPath.rbegin())));
+		pt.end<3>()=Vector3r(i<rotPath.size()?rotPath[i]:(rotPath.empty()?Vector3r::Zero():*(rotPath.rbegin())));
+		_path.push_back(pt);
+
 	}
 	// update time points from distances, repeat last distance if shorter than path
 	_pathT.clear(); _pathT.push_back(0);
@@ -94,7 +96,8 @@
 		return;
 	}
 	/* initialize or update local axes and trsf */
-	rotGeom=Vector3r(NaN,NaN,NaN); // this is meaningful only with l6geom
+	//DEL rotGeom=Vector3r(NaN,NaN,NaN); // this is meaningful only with l6geom
+	uGeom.end<3>()=Vector3r(NaN,NaN,NaN);
 	if(!l3Geom){
 		axX=gsc->normal; /* just in case */ axX.normalize();
 		if(doInit){ // initialization of the new interaction
@@ -108,7 +111,8 @@
 				scGeom->rotate(axY); scGeom->rotate(axZ);
 				scGeom->rotate(shearTot);
 				shearTot+=scGeom->shearIncrement();
-				ptGeom=Vector3r(-scGeom->penetrationDepth,shearTot.dot(axY),shearTot.dot(axZ));
+				//DEL ptGeom=Vector3r(-scGeom->penetrationDepth,shearTot.dot(axY),shearTot.dot(axZ)); 
+				uGeom.start<3>()=Vector3r(-scGeom->penetrationDepth,shearTot.dot(axY),shearTot.dot(axZ));
 			}
 			else{ // d3dGeom
 				throw runtime_error("LawTester: Dem3DofGeom not yet supported.");
@@ -122,30 +126,33 @@
 	} else {
 		trsf=l3Geom->trsf;
 		axX=trsf.row(0); axY=trsf.row(1); axZ=trsf.row(2);
-		ptGeom=l3Geom->u;
+		//DEL ptGeom=l3Geom->u;
+		uGeom.start<3>()=l3Geom->u;
 		if(l6Geom){
-			rotGeom=l6Geom->phi;
+			//rotGeom=l6Geom->phi;
+			uGeom.end<3>()=l6Geom->phi;
 			// perform allshearing by translation, as it does not induce bending
 			if(rotWeight!=0){ LOG_INFO("LawTester.rotWeight set to 0 (was"<<rotWeight<<"), since rotational DoFs are in use."); rotWeight=0; }
 		}
 	}
-	trsfQ=Quaternionr(trsf);
 	contPt=gsc->contactPoint;
 	refLength=gsc->refR1+gsc->refR2;
 	renderLength=.5*refLength;
 	
 	// here we go ahead, finally
-	Vector3r u=linearInterpolate<Vector3r,int>(step,_pathT,_pathU,_interpPos);
-	Vector3r phi=linearInterpolate<Vector3r,int>(step,_pathT,_pathR,_interpPosRot);
-	Vector3r dU=u-uPrev; uPrev=u;
-	Vector3r dPhi=phi-phiPrev; phiPrev=phi;
+	Vector6r uu=linearInterpolate<Vector6r,int>(step,_pathT,_path,_interpPos);
+	Vector6r dUU=uu-uuPrev; uuPrev=uu;
+	Vector3r dU(dUU.start<3>()), dPhi(dUU.end<3>());
+	//Vector3r dU=u-uPrev.start<3>(); uPrev.start<3>()=u;
+	//Vector3r dPhi=phi-uPrev.end<3>(); uPrev.end<3>()=phi;
 	if(displIsRel){
 		LOG_DEBUG("Relative displacement diff is "<<dU<<" (will be normalized by "<<gsc->refR1+gsc->refR2<<")");
 		dU*=refLength;
 	}
 	LOG_DEBUG("Absolute diff is: displacement "<<dU<<", rotation "<<dPhi);
-	ptOurs+=dU;
-	rotOurs+=dPhi;
+	//DEL ptOurs+=dU; rotOurs+=dPhi;
+	uTest=uTestNext; // the value that was next in the previous step is the current one now
+	uTestNext.start<3>()+=dU; uTestNext.end<3>()+=dPhi;
 
 	// reset velocities where displacement is controlled
 	//for(int i=0; i<3; i++){ if(forceControl[i]==0){ state1.vel[i]=0; state2.vel[i]=0; }
@@ -156,13 +163,13 @@
 	for(int i=0; i<2; i++){
 		int sign=(i==0?-1:1);
 		Real weight=(i==0?1-idWeight:idWeight);
-		// FIXME: this should not use refR1, but real CP-particle distance!
+		// FIXME: this should not use refR1, but real CP-particle distance perhaps?
 		Real radius=(i==0?gsc->refR1:gsc->refR2);
 		Real relRad=radius/refLength;
 		// signed and weighted displacement/rotation to be applied on this sphere (reversed for #0)
 		// some rotations must cancel the sign, by multiplying by sign again
 		Vector3r ddU=sign*dU*weight;
-		Vector3r ddPhi=sign*dPhi*(1-relRad); /* angles must distribute to both, otherwise it would induce shear; FIXME: combination of shear and bending must make sure they are properly orthogonal! (rotWeight=?) */
+		Vector3r ddPhi=sign*dPhi*(1-relRad); /* angles must distribute to both, otherwise it would induce shear; combination of shear and bending must make sure they are properly orthogonal!  */
 		vel[i]=angVel[i]=Vector3r::Zero();
 
 		// normal displacement
@@ -254,10 +261,10 @@
 	}
 
 	// put the origin to the initial (no-shear) point, so that the current point appears at the contact point
-	glTranslatev(Vector3r(0,tester->ptOurs[1],tester->ptOurs[2]));
-
-
-	const int t(tester->step); const vector<int>& TT(tester->_pathT); const vector<Vector3r>& VV(tester->_pathU);
+	glTranslatev(Vector3r(0,tester->uTestNext[1],tester->uTestNext[2]));
+
+
+	const int t(tester->step); const vector<int>& TT(tester->_pathT); const vector<Vector6r>& VV(tester->_path);
 	size_t numSegments=TT.size();
 	const Vector3r colorBefore=Vector3r(.7,1,.7), colorAfter=Vector3r(1,.7,.7);
 
@@ -267,19 +274,19 @@
 
 	// find maximum displacement, draw axes in the shear plane
 	Real displMax=0;
-	FOREACH(const Vector3r& v, VV) displMax=max(v.squaredNorm(),displMax);
+	FOREACH(const Vector6r& v, VV) displMax=max(v.start<3>().squaredNorm(),displMax);
 	displMax=1.2*scale*sqrt(displMax);
 
 	glLineWidth(1.);
 	GLUtils::GLDrawLine(Vector3r(0,-displMax,0),Vector3r(0,displMax,0),Vector3r(.5,0,0));
 	GLUtils::GLDrawLine(Vector3r(0,0,-displMax),Vector3r(0,0,displMax),Vector3r(.5,0,0));
 
-	// draw path
+	// draw displacement path
 	glLineWidth(4.);
 	for(size_t segment=0; segment<numSegments-1; segment++){
 		// different colors before and after the current point
 		Real t0=TT[segment],t1=TT[segment+1];
-		const Vector3r &from=-VV[segment]*scale, &to=-VV[segment+1]*scale;
+		const Vector3r &from=-VV[segment].start<3>()*scale, &to=-VV[segment+1].start<3>()*scale;
 		// current segment
 		if(t>t0 && t<t1){
 			Real norm=(t-t0)/(t1-t0);

=== modified file 'pkg/dem/DomainLimiter.hpp'
--- pkg/dem/DomainLimiter.hpp	2010-12-06 20:05:12 +0000
+++ pkg/dem/DomainLimiter.hpp	2010-12-09 12:38:42 +0000
@@ -19,41 +19,45 @@
 		void init();
 		virtual void action();
 		void postLoad(LawTester&);
+		void warnDeprec(const string& s1, const string& s2){ if(!warnedDeprecPtRot){ warnedDeprecPtRot=true; LOG_WARN("LawTester."<<s1<<" is deprecated, use LawTester."<<s2<<" instead.");} }
+		Vector3r get_ptOurs(){ warnDeprec("ptOurs","uTest.head()"); return uTest.start<3>(); } Vector3r get_ptGeom(){ warnDeprec("ptGeom","uGeom.head()"); return uGeom.start<3>(); }
+		Vector3r get_rotOurs(){ warnDeprec("rotOurs","uTest.tail()"); return uTest.end<3>(); }  Vector3r get_rotGeom(){ warnDeprec("rotGeom","uGeom.tail()"); return uGeom.end<3>(); }
 	DECLARE_LOGGER;
-	YADE_CLASS_BASE_DOC_ATTRS(LawTester,PartialEngine,"Prescribe and apply deformations of an interaction in terms of normal and shear displacements. See :ysrc:`scripts/test/law-test.py`. ",
-		((vector<Vector3r>,path,,Attr::triggerPostLoad,"Loading path, where each Vector3 contains desired normal displacement and two components of the shear displacement (in local coordinate system, which is being tracked automatically. If shorter than :yref:`rotPath<LawTester.rotPath>`, the last value is repeated."))
+	YADE_CLASS_BASE_DOC_ATTRS_DEPREC_INIT_CTOR_PY(LawTester,PartialEngine,"Prescribe and apply deformations of an interaction in terms of local normal and shear displacements and rotations (using either :yref:`disPpath<LawTester.disPath>` and :yref:`rotPath<LawTester.rotPath>` [or :yref:`path<LawTester.path>` in the future]). Supported :yref:`IGeom` types are :yref:`ScGeom`, :yref:`L3Geom` and :yref:`L6Geom`. \n\nSee :ysrc:`scripts/test/law-test.py` for an example.",
+		((vector<Vector3r>,disPath,,Attr::triggerPostLoad,"Loading path, where each Vector3 contains desired normal displacement and two components of the shear displacement (in local coordinate system, which is being tracked automatically. If shorter than :yref:`rotPath<LawTester.rotPath>`, the last value is repeated."))
 		((vector<Vector3r>,rotPath,,Attr::triggerPostLoad,"Rotational components of the loading path, where each item contains torsion and two bending rotations in local coordinates. If shorter than :yref:`path<LawTester.path>`, the last value is repeated."))
 		((vector<string>,hooks,,,"Python commands to be run when the corresponding point in path is reached, before doing other things in that particular step. See also :yref:`doneHook<LawTester.doneHook>`. "))
-		((Vector3r,ptOurs,Vector3r::Zero(),,"Current displacement, computed by ourselves from applied increments; should correspond to ptGeom."))
-		((Vector3r,ptGeom,Vector3r::Zero(),,"Current displacement, as computed by the geometry functor"))
-		((Vector3r,shearTot,Vector3r::Zero(),,"Current displacement in global coordinates."))
-		((Vector3r,rotOurs,Vector3r::Zero(),,"Current rotation, computed by ourselves from applied increments; should correspond to rotGeom."))
-		((Vector3r,rotGeom,Vector3r::Zero(),,"Current rotation, as computed by the geometry functor"))
-		((Vector3r,rotTot,Vector3r::Zero(),,"Current rotation in global coordinates."))
-		((bool,displIsRel,true,,"Whether displacement values in *path* are normalized by reference contact length (r1+r2 for 2 spheres)."))
+		((Vector6r,uGeom,Vector6r::Zero(),,"Current generalized displacements (3 displacements, 3 rotations), as stored in the interation itself. They should corredpond to :yref:`uTest<LawTester.uTest>`, otherwise a bug is indicated."))
+		((Vector6r,uTest,Vector6r::Zero(),,"Current generalized displacements (3 displacements, 3 rotations), as they should be according to this :yref:`LawTester`. Should correspond to :yref:`uGeom<LawTester.uGeom>`."))
+		((Vector6r,uTestNext,Vector6r::Zero(),Attr::hidden,"The value of uTest in the next step; since uTest is computed before uGeom is updated (in the next time step), uTest and uGeom would be always shifted by one timestep."))
+		((bool,warnedDeprecPtRot,false,Attr::hidden,"Flag to say that the user was already warned about using deprecated ptOurg/ptGeom/rotOurs/rotGeom."))
+		((Vector3r,shearTot,Vector3r::Zero(),Attr::hidden,"Current displacement in global coordinates; only used internally with ScGeom."))
+		((bool,displIsRel,true,,"Whether displacement values in *disPath* are normalized by reference contact length (r1+r2 for 2 spheres)."))
 		((Vector3i,forceControl,Vector3i::Zero(),,"Select which components of path (non-zero value) have force (stress) rather than displacement (strain) meaning."))
 		((vector<int>,pathSteps,((void)"(constant step)",vector<int>(1,1)),Attr::triggerPostLoad,"Step number for corresponding values in :yref:`path<LawTester.path>`; if shorter than path, distance between last 2 values is used for the rest."))
 		((vector<int>,_pathT,,(Attr::readonly|Attr::noSave),"Time value corresponding to points on path, computed from *pathSteps*. Length is always the same as path."))
-		((vector<Vector3r>,_pathU,,(Attr::readonly|Attr::noSave),"Displacement path values, computed from *path* by appending zero initial value (and possibly repeating the last value to match the length of rotation path."))
-		((vector<Vector3r>,_pathR,,(Attr::readonly|Attr::noSave),"Rotation path values, computed from *path* by appending zero initial value (and possibly repeating the last value to match the length of displacement path)."))
+		((vector<Vector6r>,_path,,(Attr::readonly|Attr::noSave),"Generalized displacement path values, computed from *disPath* and *rotPath* by appending zero initial value, and possibly repeating the last value to make lengths of *disPath* and *rotPath* match."))
 		((shared_ptr<Interaction>,I,,(Attr::hidden),"Interaction object being tracked."))
-		((Vector3r,axX,,,"Local x-axis in global coordinates (normal of the contact) |yupdate|"))
-		((Vector3r,axY,,,"Local y-axis in global coordinates; perpendicular to axX; initialized arbitrarily, but tracked to be consistent. |yupdate|"))
-		((Vector3r,axZ,,Attr::noSave,"Local z-axis in global coordinates; computed from axX and axY. |yupdate|"))
-		((Matrix3r,trsf,,Attr::noSave,"Transformation matrix for the local coordinate system. |yupdate|"))
-		((size_t,_interpPos,0,(Attr::readonly|Attr::hidden),"State cookie for the interpolation routine (displacements)."))
-		((size_t,_interpPosRot,0,(Attr::readonly|Attr::hidden),"State cookie for the interpolation routine (rotations)."))
-		((Vector3r,uPrev,Vector3r::Zero(),(Attr::readonly),"Displacement value reached in the previous step."))
-		((Vector3r,phiPrev,Vector3r::Zero(),(Attr::readonly),"Rotation value reached in the previous step."))
-		((Quaternionr,trsfQ,,Attr::noSave,"Transformation quaterion for the local coordinate system. |yupdate|"))
+
+		// axX, axY, axZ could be replaced by references to rows in trsf; perhaps do that in the future (in such a case, trsf would not be noSave)
+		((Vector3r,axX,,Attr::hidden,"Local x-axis in global coordinates (normal of the contact) |yupdate|"))
+		((Vector3r,axY,,Attr::hidden,"Local y-axis in global coordinates; perpendicular to axX; initialized arbitrarily, but tracked to be consistent. |yupdate|"))
+		((Vector3r,axZ,,(Attr::hidden|Attr::noSave),"Local z-axis in global coordinates; computed from axX and axY. |yupdate|"))
+		((Matrix3r,trsf,,(Attr::noSave|Attr::readonly),"Transformation matrix for the local coordinate system. |yupdate|"))
+		((size_t,_interpPos,0,(Attr::readonly|Attr::hidden),"State cookie for the interpolation routine."))
+		((Vector6r,uuPrev,Vector6r::Zero(),(Attr::readonly),"Generalized displacement values reached in the previous step, for knowing which increment to apply in the current step."))
 		((int,step,0,,"Step number in which this engine is active; determines position in path, using pathSteps."))
 		((string,doneHook,,,"Python command (as string) to run when end of the path is achieved. If empty, the engine will be set :yref:`dead<Engine.dead>`."))
 		((Real,renderLength,0,,"Characteristic length for the purposes of rendering, set equal to the smaller radius."))
 		((Real,refLength,0,(Attr::readonly),"Reference contact length, for rendering only."))
-		((Vector3r,contPt,Vector3r::Zero(),,"Contact point (for rendering only)"))
+		((Vector3r,contPt,Vector3r::Zero(),Attr::hidden,"Contact point (for rendering only)"))
 		((Real,idWeight,1,,"Float ∈〈0,1〉 determining on which particle are displacements applied (0 for id1, 1 for id2); intermediate values will apply respective part to each of them."))
 		((Real,rotWeight,1,,"Float ∈〈0,1〉 determining whether shear displacement is applied as rotation or displacement on arc (0 is displacement-only, 1 is rotation-only). Not effective when mutual rotation is specified."))
 		// reset force components along individual axes, instead of blocking DOFs which have no specific direction (for the force control)
+		, /* deprec */ ((path,disPath,"LawTester.path will be used for generalized displacement (6-component) loading path in the future."))
+		, /* init */
+		, /* ctor */
+		, /* py */ .add_property("ptOurs",&LawTester::get_ptOurs) .add_property("ptGeom",&LawTester::get_ptGeom) .add_property("rotOurs",&LawTester::get_rotOurs) .add_property("rotGeom",&LawTester::get_rotGeom)
 	);
 };
 REGISTER_SERIALIZABLE(LawTester);

=== modified file 'py/mathWrap/miniEigen.cpp'
--- py/mathWrap/miniEigen.cpp	2010-11-07 11:46:20 +0000
+++ py/mathWrap/miniEigen.cpp	2010-12-09 12:38:42 +0000
@@ -104,6 +104,10 @@
 static Real Vector2i_dot(const Vector2i& self, const Vector2i& v){ return self.dot(v); }
 static Vector3r Vector3r_cross(const Vector3r& self, const Vector3r& v){ return self.cross(v); }
 static Vector3i Vector3i_cross(const Vector3i& self, const Vector3i& v){ return self.cross(v); }
+static Vector3r Vector6r_head(const Vector6r& self){ return self.start<3>(); }
+static Vector3r Vector6r_tail(const Vector6r& self){ return self.end<3>(); }
+static Vector3i Vector6i_head(const Vector6i& self){ return self.start<3>(); }
+static Vector3i Vector6i_tail(const Vector6i& self){ return self.end<3>(); }
 static bool Quaternionr__eq__(const Quaternionr& q1, const Quaternionr& q2){ return q1==q2; }
 static bool Quaternionr__neq__(const Quaternionr& q1, const Quaternionr& q2){ return q1!=q2; }
 #include<Eigen/SVD>
@@ -262,6 +266,7 @@
 		// methods
 		//.def("dot",&Vector6r_dot).def("cross",&Vector6r_cross)
 		.def("norm",&Vector6r::norm).def("squaredNorm",&Vector6r::squaredNorm).def("normalize",&Vector6r::normalize).def("normalized",&Vector6r::normalized)
+		.def("head",&Vector6r_head).def("tail",&Vector6r_tail)
 		// operators
 		.def("__neg__",&Vector6r__neg__) // -v
 		.def("__add__",&Vector6r__add__Vector6r).def("__iadd__",&Vector6r__iadd__Vector6r) // +, +=
@@ -289,6 +294,7 @@
 		// methods
 		//.def("dot",&Vector6i_dot).def("cross",&Vector6i_cross)
 		.def("norm",&Vector6i::norm).def("squaredNorm",&Vector6i::squaredNorm).def("normalize",&Vector6i::normalize).def("normalized",&Vector6i::normalized)
+		.def("head",&Vector6i_head).def("tail",&Vector6i_tail)
 		// operators
 		.def("__neg__",&Vector6i__neg__) // -v
 		.def("__add__",&Vector6i__add__Vector6i).def("__iadd__",&Vector6i__iadd__Vector6i) // +, +=

=== modified file 'py/tests/omega.py'
--- py/tests/omega.py	2010-11-19 15:29:34 +0000
+++ py/tests/omega.py	2010-12-09 12:38:42 +0000
@@ -99,8 +99,8 @@
 			O.reset()
 			try:
 				O.miscParams=[eval(c)()]
-				O.saveTmp()
-				O.loadTmp()
+				O.saveTmp(quiet=True)
+				O.loadTmp(quiet=True)
 			except (RuntimeError,ValueError):
 				failed.add(c)
 		failed=list(failed); failed.sort()

=== modified file 'py/wrapper/customConverters.cpp'
--- py/wrapper/customConverters.cpp	2010-12-06 20:05:12 +0000
+++ py/wrapper/customConverters.cpp	2010-12-09 12:38:42 +0000
@@ -214,7 +214,12 @@
 		VECTOR_SEQ_CONV(Real);
 		VECTOR_SEQ_CONV(Se3r);
 		VECTOR_SEQ_CONV(Vector2r);
+		VECTOR_SEQ_CONV(Vector2i);
 		VECTOR_SEQ_CONV(Vector3r);
+		VECTOR_SEQ_CONV(Vector3i);
+		VECTOR_SEQ_CONV(Vector6r);
+		VECTOR_SEQ_CONV(Vector6i);
+		VECTOR_SEQ_CONV(Matrix3r);
 		VECTOR_SEQ_CONV(std::string);
 		VECTOR_SEQ_CONV(shared_ptr<Body>);
 		VECTOR_SEQ_CONV(shared_ptr<Engine>);

=== modified file 'py/wrapper/yadeWrapper.cpp'
--- py/wrapper/yadeWrapper.cpp	2010-12-05 17:10:06 +0000
+++ py/wrapper/yadeWrapper.cpp	2010-12-09 12:38:42 +0000
@@ -55,6 +55,7 @@
 
 using namespace boost;
 using namespace std;
+namespace py = boost::python;
 
 #include<yade/lib/serialization/ObjectIO.hpp>
 
@@ -368,16 +369,15 @@
 	}
 	bool isRunning(){ return OMEGA.isRunning(); }
 	python::object get_filename(){ string f=OMEGA.sceneFile; if(f.size()>0) return python::object(f); return python::object();}
-	void load(std::string fileName) {
+	void load(std::string fileName,bool quiet=false) {
 		Py_BEGIN_ALLOW_THREADS; OMEGA.stop(); Py_END_ALLOW_THREADS; 
-		OMEGA.loadSimulation(fileName);
+		OMEGA.loadSimulation(fileName,quiet);
 		OMEGA.createSimulationLoop();
 		mapLabeledEntitiesToVariables();
-		LOG_DEBUG("LOAD!");
 	}
 	void reload(){	load(OMEGA.sceneFile);}
-	void saveTmp(string mark=""){ save(":memory:"+mark);}
-	void loadTmp(string mark=""){ load(":memory:"+mark);}
+	void saveTmp(string mark="", bool quiet=false){ save(":memory:"+mark,quiet);}
+	void loadTmp(string mark="", bool quiet=false){ load(":memory:"+mark,quiet);}
 	python::list lsTmp(){ python::list ret; typedef pair<std::string,string> strstr; FOREACH(const strstr& sim,OMEGA.memSavedSimulations){ string mark=sim.first; boost::algorithm::replace_first(mark,":memory:",""); ret.append(mark); } return ret; }
 	void tmpToFile(string mark, string filename){
 		if(OMEGA.memSavedSimulations.count(":memory:"+mark)==0) throw runtime_error("No memory-saved simulation named "+mark);
@@ -399,11 +399,10 @@
 	void switchScene(){ std::swap(OMEGA.scene,OMEGA.sceneAnother); }
 	shared_ptr<Scene> scene_get(){ return OMEGA.getScene(); }
 
-	void save(std::string fileName){
+	void save(std::string fileName,bool quiet=false){
 		assertScene();
-		OMEGA.saveSimulation(fileName);
+		OMEGA.saveSimulation(fileName,quiet);
 		// OMEGA.sceneFile=fileName; // done in Omega::saveSimulation;
-		LOG_DEBUG("SAVE!");
 	}
 	
 	python::list miscParams_get(){
@@ -531,11 +530,11 @@
 		.add_property("dt",&pyOmega::dt_get,&pyOmega::dt_set,"Current timestep (Δt) value.\n\n* assigning negative value enables dynamic Δt (by looking for a :yref:`TimeStepper` in :yref:`O.engine<Omega.engines>`) and sets positive timestep ``O.dt=|Δt|`` (will be used until the timestepper is run and updates it)\n* assigning positive value sets Δt to that value and disables dynamic Δt (via :yref:`TimeStepper`, if there is one).\n\n:yref:`dynDt<Omega.dynDt>` can be used to query whether dynamic Δt is in use.")
 		.add_property("dynDt",&pyOmega::dynDt_get,"Whether a :yref:`TimeStepper` is used for dynamic Δt control. See :yref:`dt<Omega.dt>` on how to enable/disable :yref:`TimeStepper`.")
 		.add_property("dynDtAvailable",&pyOmega::dynDtAvailable_get,"Whether a :yref:`TimeStepper` is amongst :yref:`O.engines<Omega.engines>`, activated or not.")
-		.def("load",&pyOmega::load,"Load simulation from file.")
+		.def("load",&pyOmega::load,(py::arg("file"),py::arg("quiet")=false),"Load simulation from file.")
 		.def("reload",&pyOmega::reload,"Reload current simulation")
-		.def("save",&pyOmega::save,"Save current simulation to file (should be .xml or .xml.bz2)")
-		.def("loadTmp",&pyOmega::loadTmp,(python::args("mark")=""),"Load simulation previously stored in memory by saveTmp. *mark* optionally distinguishes multiple saved simulations")
-		.def("saveTmp",&pyOmega::saveTmp,(python::args("mark")=""),"Save simulation to memory (disappears at shutdown), can be loaded later with loadTmp. *mark* optionally distinguishes different memory-saved simulations.")
+		.def("save",&pyOmega::save,(py::arg("file"),py::arg("quiet")=false),"Save current simulation to file (should be .xml or .xml.bz2)")
+		.def("loadTmp",&pyOmega::loadTmp,(py::arg("mark")="",py::arg("quiet")=false),"Load simulation previously stored in memory by saveTmp. *mark* optionally distinguishes multiple saved simulations")
+		.def("saveTmp",&pyOmega::saveTmp,(py::arg("mark")="",py::arg("quiet")=false),"Save simulation to memory (disappears at shutdown), can be loaded later with loadTmp. *mark* optionally distinguishes different memory-saved simulations.")
 		.def("lsTmp",&pyOmega::lsTmp,"Return list of all memory-saved simulations.")
 		.def("tmpToFile",&pyOmega::tmpToFile,(python::arg("fileName"),python::arg("mark")=""),"Save XML of :yref:`saveTmp<Omega.saveTmp>`'d simulation into *fileName*.")
 		.def("tmpToString",&pyOmega::tmpToString,(python::arg("mark")=""),"Return XML of :yref:`saveTmp<Omega.saveTmp>`'d simulation as string.")