← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-pkg/yade/git-trunk] Rev 3686: Tetra modification (Ig2_Tetra_Tetra_TTtraSimpleGeom, Law2_TTetraSimpleGeom_NormPhys_Simple, modif...

 

------------------------------------------------------------
revno: 3686
committer: Jan Stransky <jan.stransky@xxxxxxxxxxx>
timestamp: Sun 2013-09-08 13:12:42 +0200
message:
  Tetra modification (Ig2_Tetra_Tetra_TTtraSimpleGeom, Law2_TTetraSimpleGeom_NormPhys_Simple, modified GL functor, examples, utils.tetra)
  added Ip2_ElastMat_... functors
  fixed bug in utils.kineticEnergy for aspherical bodies
added:
  examples/polyhedra/
  examples/polyhedra/oneTetra.py
  examples/polyhedra/twoTetras.py
  pkg/dem/Ip2_ElastMat.cpp
  pkg/dem/Ip2_ElastMat.hpp
modified:
  lib/opengl/OpenGLWrapper.hpp
  pkg/common/Facet.cpp
  pkg/common/Facet.hpp
  pkg/dem/Shop.cpp
  pkg/dem/Shop.hpp
  pkg/dem/Tetra.cpp
  pkg/dem/Tetra.hpp
  py/_utils.cpp
  py/utils.py


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

Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-pkg/yade/git-trunk/+edit-subscription
=== added directory 'examples/polyhedra'
=== added file 'examples/polyhedra/oneTetra.py'
--- examples/polyhedra/oneTetra.py	1970-01-01 00:00:00 +0000
+++ examples/polyhedra/oneTetra.py	2013-09-08 11:12:42 +0000
@@ -0,0 +1,42 @@
+################################################################################
+# 
+# Script to test tetra gl functions and prescribe dmotion
+#
+################################################################################
+v1 = (0,0,0)
+v2 = (1,0,0)
+v3 = (0,1,0)
+v4 = (0,0,1)
+
+t1 = tetra((v1,v2,v3,v4),color=(0,1,0))
+O.bodies.append((t1))
+
+def changeVelocity():
+	if O.iter == 50000:
+		t1.state.vel = (-1,0,0)
+	if O.iter == 100000:
+		t1.state.vel = (0,1,-1)
+	if O.iter == 150000:
+		t1.state.vel = (0,0,0)
+		t1.state.angMom = (0,0,10)
+	if O.iter == 200000:
+		O.pause()
+
+O.engines = [
+	ForceResetter(),
+	InsertionSortCollider([Bo1_Tetra_Aabb()]),
+	InteractionLoop([],[],[]),
+	NewtonIntegrator(),
+	PyRunner(iterPeriod=1,command="changeVelocity()"),
+]
+
+
+try:
+	from yade import qt
+	qt.View()
+except:
+	pass
+
+O.dt = 1e-5
+t1.state.vel = (1,0,0)
+O.run()

=== added file 'examples/polyhedra/twoTetras.py'
--- examples/polyhedra/twoTetras.py	1970-01-01 00:00:00 +0000
+++ examples/polyhedra/twoTetras.py	2013-09-08 11:12:42 +0000
@@ -0,0 +1,111 @@
+################################################################################
+#
+# Python script to test tetra-tetra contact detection for different possible
+# contact modes. Some tests are run twice to test the symmetry of the law.
+#
+# It runs several tests, making pause before each one. If run with GUI, you can
+# adjust the viewer
+#
+# During the test, momentum, angular momentum and kinetic energy is tracked in
+# plot.data. You can use plot.plot() to see the results (in sime modes the
+# energy is not conserved for instace).
+#
+################################################################################
+from yade import qt,plot
+
+O.materials.append(ElastMat(young=1e10))
+O.dt = 4e-5
+O.engines = [
+	ForceResetter(),
+	InsertionSortCollider([Bo1_Tetra_Aabb()]),
+	InteractionLoop(
+		[Ig2_Tetra_Tetra_TTetraSimpleGeom()],
+		[Ip2_ElastMat_ElastMat_NormPhys()],
+		[Law2_TTetraSimpleGeom_NormPhys_Simple()]
+	),
+	NewtonIntegrator(damping=0),
+	PyRunner(iterPeriod=500,command="addPlotData()"),
+	PyRunner(iterPeriod=1000,command="printt()"),
+	PyRunner(iterPeriod=1,command="runExamples()"),
+]
+
+def addPlotData():
+	p = utils.momentum()
+	l = utils.angularMomentum()
+	plot.addData(
+		i = O.iter,
+		e = utils.kineticEnergy(),
+		px = p[0],
+		py = p[1],
+		pz = p[2],
+		lx = l[0],
+		ly = l[1],
+		lz = l[2],
+	)
+
+def prepareExample(vertices1,vertices2,vel1=(0,0,0),vel2=(0,0,0),amom1=(0,0,0),amom2=(0,0,0),label=''):
+	O.interactions.clear()
+	O.bodies.clear()
+	t1 = tetra(vertices1,color=(0,1,0),wire=False)
+	t2 = tetra(vertices2,color=(0,1,1),wire=False)
+	O.bodies.append((t1,t2))
+	t1.state.vel = vel1
+	t2.state.vel = vel2
+	t1.state.angMom = amom1
+	t2.state.angMom = amom2
+	if label: print label
+	O.pause()
+	O.step()
+
+def runExamples():
+	dt = 20000
+	if O.iter == 1:
+		vertices1 = ((0,0,0),(2,0,0),(0,2,0),(0,0,2))
+		vertices2 = ((1,1,1),(3,1,1),(1,3,1),(1,1,3))
+		prepareExample(vertices1,vertices2,vel2=(-1,-1,-1),label='\ntesting vertex-triangle contact...\n')
+	if O.iter == 1*dt:
+		plot.data = {}
+		vertices1 = ((1,1,1),(3,1,1),(1,3,1),(1,1,3))
+		vertices2 = ((0,0,0),(2,0,0),(0,2,0),(0,0,2))
+		prepareExample(vertices1,vertices2,vel1=(-1,-1,-1),label='\ntesting vertex-triangle contact 2...\n')
+	elif O.iter == 2*dt:
+		vertices1 = ((0,0,0),(0,0,1.1),(1,0,1),(0,1,.9))
+		vertices2 = ((0,.5,1.4),(-.5,.5,.6),(-1.6,0,1),(-1.6,1.1,1))
+		prepareExample(vertices1,vertices2,vel2=(1,0,0),amom2=(0,10,0),label='\ntesting edge-edge contact\n')
+	elif O.iter == 3*dt:
+		vertices1 = ((0,0,0),(0,0,1.1),(1,0,1),(0,1,.9))
+		vertices2 = ((-.5,.5,.6),(0,.5,1.4),(-1.6,1.1,1),(-1.6,0,1))
+		prepareExample(vertices1,vertices2,vel2=(1,0,0),amom2=(0,10,0),label='\ntesting edge-edge contact\n')
+	elif O.iter == 4*dt:
+		vertices1 = ((.1,-.4,-.3),(-.3,-.4,2),(3,-.2,2),(-.1,3,2))
+		vertices2 = ((.5,1.5,2.3),(1.5,.5,2.3),(2,2,3),(0,0,3))
+		prepareExample(vertices1,vertices2,vel2=(0,0,-1),amom2=(0,0,0),label='\ntesting edge-triangle contact\n')
+	elif O.iter == 5*dt:
+		vertices1 = ((.1,-.4,-.3),(-.3,-.4,2),(3,-.2,2),(-.1,3,2))
+		vertices2 = ((-.5,2.5,2.3),(2.5,-.5,2.3),(2,2,3),(0,0,3))
+		prepareExample(vertices1,vertices2,vel2=(0,0,-1),amom2=(0,0,0),label='\ntesting edge-triangle contact 2\n')
+	elif O.iter == 6*dt:
+		vertices1 = ((1,0,0),(0,1,0),(0,0,1),(1,1,1))
+		vertices2 = ((.5,.5,1.2),(0,.1,2),(2,0,2),(0,1,2))
+		prepareExample(vertices1,vertices2,vel2=(0,0,-1),label='\ntesting vertex-edge contact\n')
+	elif O.iter == 7*dt:
+		vertices1 = ((.5,.5,1.2),(0,.1,2),(2,0,2),(0,1,2))
+		vertices2 = ((1,0,0),(0,1,0),(0,0,1),(1,1,1))
+		prepareExample(vertices1,vertices2,vel1=(0,0,-1),label='\ntesting vertex-edge contact 2\n')
+	elif O.iter == 8*dt:
+		vertices1 = ((0,0,0),(1,0,0),(0,1,0),(0,0,1))
+		vertices2 = ((0,0,1.2),(.9,.8,2),(-.7,.61,2.3),(0,-.7,2.1))
+		prepareExample(vertices1,vertices2,vel2=(0,0,-1),label='\ntesting vertex-edge contact\n')
+	elif O.iter == 9*dt:
+		vertices1 = ((0,0,1.2),(.9,.8,2),(-.7,.61,2.3),(0,-.7,2.1))
+		vertices2 = ((0,0,0),(1,0,0),(0,1,0),(0,0,1))
+		prepareExample(vertices1,vertices2,vel1=(0,0,-1),label='\ntesting vertex-edge contact 2\n')
+	elif O.iter == 10*dt:
+		O.pause()
+
+viewer = qt.View()
+plot.plots = {'i':'e','i ':('px','py','pz'),'i  ':('lx','ly','lz')}
+
+O.step()
+O.step()
+O.step()

=== modified file 'lib/opengl/OpenGLWrapper.hpp'
--- lib/opengl/OpenGLWrapper.hpp	2012-08-02 18:13:22 +0000
+++ lib/opengl/OpenGLWrapper.hpp	2013-09-08 11:12:42 +0000
@@ -26,71 +26,76 @@
 
 ///	Primary Templates
 
-template< typename Type > inline void glRotate		( Type ,Type ,Type , Type  )	{	STATIC_ASSERT(false);  };
-template< typename Type > inline void glScale		( Type ,Type , Type  )		{	STATIC_ASSERT(false); };
+template< typename Type > inline void glRotate		( Type, Type, Type,  Type  )	{	STATIC_ASSERT(false);  };
+template< typename Type > inline void glScale		( Type, Type,  Type  )		{	STATIC_ASSERT(false); };
 template< typename Type > inline void glScalev		( const Type  )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glTranslate	( Type ,Type , Type  )		{	STATIC_ASSERT(false); };
+template< typename Type > inline void glTranslate	( Type, Type,  Type  )		{	STATIC_ASSERT(false); };
 template< typename Type > inline void glTranslatev	( const Type )			{	STATIC_ASSERT(false);  };
-template< typename Type > inline void glVertex2		( Type ,Type  )			{	STATIC_ASSERT(false);  };
-template< typename Type > inline void glVertex3		( Type ,Type , Type  )		{	STATIC_ASSERT(false);  };
-template< typename Type > inline void glVertex4		( Type ,Type ,Type , Type  )	{	STATIC_ASSERT(false); };
+template< typename Type > inline void glVertex2		( Type, Type  )			{	STATIC_ASSERT(false);  };
+template< typename Type > inline void glVertex3		( Type, Type,  Type  )		{	STATIC_ASSERT(false);  };
+template< typename Type > inline void glVertex4		( Type, Type, Type,  Type  )	{	STATIC_ASSERT(false); };
 template< typename Type > inline void glVertex2v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glVertex3v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glVertex4v	( const Type )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glNormal3		( Type ,Type ,Type  )		{	STATIC_ASSERT(false); };
+template< typename Type > inline void glNormal3		( Type, Type, Type  )		{	STATIC_ASSERT(false); };
 template< typename Type > inline void glNormal3v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glIndex		( Type  )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glIndexv		( Type  )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glColor3		( Type ,Type ,Type  )		{	STATIC_ASSERT(false); };
-template< typename Type > inline void glColor4		( Type ,Type ,Type , Type  )	{	STATIC_ASSERT(false); };
+template< typename Type > inline void glColor3		( Type, Type, Type  )		{	STATIC_ASSERT(false); };
+template< typename Type > inline void glColor4		( Type, Type, Type,  Type  )	{	STATIC_ASSERT(false); };
 template< typename Type > inline void glColor3v		( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glColor4v		( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glTexCoord1	( Type  )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glTexCoord2	( Type ,Type  )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glTexCoord3	( Type ,Type , Type  )		{	STATIC_ASSERT(false); };
-template< typename Type > inline void glTexCoord4	( Type ,Type ,Type , Type  )	{	STATIC_ASSERT(false); };
+template< typename Type > inline void glTexCoord2	( Type, Type  )			{	STATIC_ASSERT(false); };
+template< typename Type > inline void glTexCoord3	( Type, Type,  Type  )		{	STATIC_ASSERT(false); };
+template< typename Type > inline void glTexCoord4	( Type, Type, Type,  Type  )	{	STATIC_ASSERT(false); };
 template< typename Type > inline void glTexCoord1v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glTexCoord2v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glTexCoord3v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glTexCoord4v	( const Type )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glRasterPos2	( Type ,Type  )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glRasterPos3	( Type ,Type , Type  )		{	STATIC_ASSERT(false); };
-template< typename Type > inline void glRasterPos4	( Type ,Type ,Type , Type  )	{	STATIC_ASSERT(false); };
+template< typename Type > inline void glRasterPos2	( Type, Type  )			{	STATIC_ASSERT(false); };
+template< typename Type > inline void glRasterPos3	( Type, Type,  Type  )		{	STATIC_ASSERT(false); };
+template< typename Type > inline void glRasterPos4	( Type, Type, Type,  Type  )	{	STATIC_ASSERT(false); };
 template< typename Type > inline void glRasterPos2v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glRasterPos3v	( const Type )			{	STATIC_ASSERT(false); };
 template< typename Type > inline void glRasterPos4v	( const Type )			{	STATIC_ASSERT(false); };
-template< typename Type > inline void glRect		( Type ,Type ,Type , Type  )	{	STATIC_ASSERT(false); };
+template< typename Type > inline void glRect		( Type, Type, Type,  Type  )	{	STATIC_ASSERT(false); };
 template< typename Type > inline void glMaterial	( GLenum face, GLenum pname, Type param ){	STATIC_ASSERT(false); };
 template< typename Type > inline void glMaterialv	( GLenum face, GLenum pname, Type param ){	STATIC_ASSERT(false); };
 template< typename Type > inline void glMultMatrix	(const Type*){	STATIC_ASSERT(false); };
 
+#define LDOUBL long double
 
 ///	Template Specializations
 template< > inline void glMultMatrix<double>(const double* m){glMultMatrixd(m);	};
-template< > inline void glMultMatrix<long double>(const long double* m){double mm[16]; for(int i=0;i<16;i++)mm[i]=(double)m[i]; glMultMatrixd(mm);};
+template< > inline void glMultMatrix<LDOUBL>(const LDOUBL* m){double mm[16]; for(int i=0;i<16;i++)mm[i]=(double)m[i]; glMultMatrixd(mm);};
 
 template< > inline void glRotate< double >			(double angle,double x,double y, double z )	{	glRotated(angle,x,y,z);	};
+template< > inline void glRotate< LDOUBL >		(LDOUBL angle, LDOUBL x, LDOUBL y, LDOUBL z )	{	glRotated((double)angle,(double)x,(double)y,(double)z);	};
 template< > inline void glRotate< float >			(float angle,float x,float y, float z )	{	glRotatef(angle,x,y,z);	};
 
 template< > inline void glScale< double >			( double x,double y, double z )		{	glScaled(x,y,z);	};
-template< > inline void glScale< long double >			( long double x,long double y, long double z )		{	glScaled(x,y,z);	};
+template< > inline void glScale< LDOUBL >			( LDOUBL x,LDOUBL y, LDOUBL z )		{	glScaled(x,y,z);	};
 template< > inline void glScale< float >			( float x,float y,float z )		{	glScalef(x,y,z);	};
 template< > inline void glScalev< Vector3r >		( const Vector3r v )		{	glScaled(v[0],v[1],v[2]);};
 
 template< > inline void glTranslate< double >			( double x,double y, double z )		{	glTranslated(x,y,z);	};
-template< > inline void glTranslate< long double >			( long double x, long double y, long double z )		{	glTranslated(x,y,z);	};
+template< > inline void glTranslate< LDOUBL >			( LDOUBL x, LDOUBL y, LDOUBL z )		{	glTranslated(x,y,z);	};
 template< > inline void glTranslate< float >			( float x,float y,float z )		{	glTranslatef(x,y,z);	};
 template< > inline void glTranslatev< Vector3r >		( const Vector3r v )		{	glTranslated(v[0],v[1],v[2]);};
 
 template< > inline void glVertex2< double >			( double x,double y )			{	glVertex2d(x,y);	};
+template< > inline void glVertex2< LDOUBL >			( LDOUBL x, LDOUBL y )			{	glVertex2d((double)x,(double)y);	};
 template< > inline void glVertex2< float >			( float x,float y )			{	glVertex2f(x,y);	};
 template< > inline void glVertex2< int >			( int x,int y )				{	glVertex2i(x,y);	};
 
 template< > inline void glVertex3< double >			( double x,double y, double z )		{	glVertex3d(x,y,z);	};
+template< > inline void glVertex3< LDOUBL >			( LDOUBL x, LDOUBL y, LDOUBL z )		{	glVertex3d((double)x,(double)y,(double)z);	};
 template< > inline void glVertex3< float >			( float x,float y,float z )		{	glVertex3f(x,y,z);	};
 template< > inline void glVertex3< int >			( int x, int y, int z )			{	glVertex3i(x,y,z);	};
 
 template< > inline void glVertex4< double >			( double x,double y,double z, double w ){	glVertex4d(x,y,z,w);	};
+template< > inline void glVertex4< LDOUBL >			( LDOUBL x, LDOUBL y, LDOUBL z, LDOUBL w ){	glVertex4d((double)x,(double)y,(double)z,(double)w);	};
 template< > inline void glVertex4< float >			( float x,float y,float z, float w )	{	glVertex4f(x,y,z,w);	};
 template< > inline void glVertex4< int >			( int x,int y,int z, int w )		{	glVertex4i(x,y,z,w);	};
 
@@ -105,12 +110,14 @@
 template< > inline void glVertex4v< Vector3i >		( const Vector3i v )		{	glVertex4iv((int*)&v);		};
 
 template< > inline void glNormal3< double >			(double nx,double ny,double nz )	{	glNormal3d(nx,ny,nz);	};
+template< > inline void glNormal3< LDOUBL >			( LDOUBL nx, LDOUBL ny, LDOUBL nz )	{	glNormal3d((double)nx,(double)ny,(double)nz);	};
 template< > inline void glNormal3< int >			(int nx,int ny,int nz )			{	glNormal3i(nx,ny,nz);	};
 
 template< > inline void glNormal3v< Vector3r >		( const Vector3r v )		{	glNormal3dv((double*)&v);		};
 template< > inline void glNormal3v< Vector3i >		( const Vector3i v )		{	glNormal3iv((int*)&v);		};
 
 template< > inline void glIndex< double >			( double c )				{	glIndexd(c);	};
+template< > inline void glIndex< LDOUBL >			( LDOUBL c )				{	glIndexd((double)c);	};
 template< > inline void glIndex< float >			( float c )				{	glIndexf(c);	};
 template< > inline void glIndex< int >				( int c )				{	glIndexi(c);	};
 template< > inline void glIndex< unsigned char >		( unsigned char c )			{	glIndexub(c);	};
@@ -119,10 +126,12 @@
 template< > inline void glIndexv<const Vector3i >		( const Vector3i c)			{	glIndexiv((int*)&c);	}
 
 template< > inline void glColor3< double >			(double red,double green,double blue )							{	glColor3d(red,green,blue);	};
+template< > inline void glColor3< LDOUBL >			( LDOUBL red, LDOUBL green, LDOUBL blue )							{	glColor3d((double)red,(double)green,(double)blue);	};
 template< > inline void glColor3< float >			(float red,float green,float blue )							{	glColor3f(red,green,blue);	};
 template< > inline void glColor3< int >			(int red,int green,int blue )								{	glColor3i(red,green,blue);	};
 
 template< > inline void glColor4< double >			(double red,double green,double blue, double alpha )					{	glColor4d(red,green,blue,alpha);	};
+template< > inline void glColor4< LDOUBL >			(LDOUBL red,LDOUBL green,LDOUBL blue, LDOUBL alpha )					{	glColor4d((double)red,(double)green,(double)blue,(double)alpha);	};
 template< > inline void glColor4< float >			(float red,float green,float blue, float alpha )					{	glColor4f(red,green,blue,alpha);	};
 template< > inline void glColor4< int >			(int red,int green,int blue, int alpha )						{	glColor4i(red,green,blue,alpha);	};
 
@@ -135,18 +144,22 @@
 
 
 template< > inline void glTexCoord1< double >			( double s )				{	glTexCoord1d(s);	};
+template< > inline void glTexCoord1< LDOUBL >			( LDOUBL s )				{	glTexCoord1d((double)s);	};
 template< > inline void glTexCoord1< float >			( float s )				{	glTexCoord1f(s);	};
 template< > inline void glTexCoord1< int >			( int s )				{	glTexCoord1i(s);	};
 
 template< > inline void glTexCoord2< double >			( double s,double t )			{	glTexCoord2d(s,t);	};
+template< > inline void glTexCoord2< LDOUBL >			( LDOUBL s,LDOUBL t )			{	glTexCoord2d((double)s,(double)t);	};
 template< > inline void glTexCoord2< float >			( float s,float t )			{	glTexCoord2f(s,t);	};
 template< > inline void glTexCoord2< int >			( int s,int t )				{	glTexCoord2i(s,t);	};
 
 template< > inline void glTexCoord3< double >			( double s,double t, double r )		{	glTexCoord3d(s,t,r);	};
+template< > inline void glTexCoord3< LDOUBL >			( LDOUBL s,LDOUBL t, LDOUBL r )		{	glTexCoord3d((double)s,(double)t,(double)r);	};
 template< > inline void glTexCoord3< float >			( float s,float t,float r )		{	glTexCoord3f(s,t,r);	};
 template< > inline void glTexCoord3< int >			( int s, int t, int r )			{	glTexCoord3i(s,t,r);	};
 
 template< > inline void glTexCoord4< double >			(double s,double t,double r, double q )	{	glTexCoord4d(s,t,r,q);	};
+template< > inline void glTexCoord4< LDOUBL >			(LDOUBL s,LDOUBL t,LDOUBL r, LDOUBL q )	{	glTexCoord4d((double)s,(double)t,(double)r,(double)q);	};
 template< > inline void glTexCoord4< float >			(float s,float t,float r, float q )	{	glTexCoord4f(s,t,r,q);	};
 template< > inline void glTexCoord4< int >			(int s,int t,int r, int q )		{	glTexCoord4i(s,t,r,q);	};
 
@@ -164,14 +177,17 @@
 
 
 template< > inline void glRasterPos2< double >			( double x,double y )			{	glRasterPos2d(x,y);	};
+template< > inline void glRasterPos2< LDOUBL >			( LDOUBL x,LDOUBL y )			{	glRasterPos2d((double)x,(double)y);	};
 template< > inline void glRasterPos2< float >			( float x,float y )			{	glRasterPos2f(x,y);	};
 template< > inline void glRasterPos2< int >			( int x,int y )				{	glRasterPos2i(x,y);	};
 
 template< > inline void glRasterPos3< double >			( double x,double y, double z )		{	glRasterPos3d(x,y,z);	};
+template< > inline void glRasterPos3< LDOUBL >			( LDOUBL x,LDOUBL y, LDOUBL z )		{	glRasterPos3d((double)x,(double)y,(double)z);	};
 template< > inline void glRasterPos3< float >			( float x,float y,float z )		{	glRasterPos3f(x,y,z);	};
 template< > inline void glRasterPos3< int >			( int x, int y, int z )			{	glRasterPos3i(x,y,z);	};
 
 template< > inline void glRasterPos4< double >			(double x,double y,double z, double w )	{	glRasterPos4d(x,y,z,w);	};
+template< > inline void glRasterPos4< LDOUBL >			(LDOUBL x,LDOUBL y,LDOUBL z, LDOUBL w )	{	glRasterPos4d((double)x,(double)y,(double)z,(double)w);	};
 template< > inline void glRasterPos4< float >			(float x,float y,float z, float w )	{	glRasterPos4f(x,y,z,w);	};
 template< > inline void glRasterPos4< int >			(int x,int y,int z, int w )		{	glRasterPos4i(x,y,z,w);	};
 
@@ -188,6 +204,7 @@
 
 
 template< > inline void glRect< double >			(double x1,double y1,double x2, double y2 )	{	glRectd(x1,y1,x2,y2);	};
+template< > inline void glRect< LDOUBL >			(LDOUBL x1,LDOUBL y1,LDOUBL x2, LDOUBL y2 )	{	glRectd((double)x1,(double)y1,(double)x2,(double)y2);	};
 template< > inline void glRect< float >			(float	x1,float  y1,float x2,float y2 )	{	glRectf(x1,y1,x2,y2);	};
 template< > inline void glRect< int >				(int	x1,int	  y1,int x2, int y2 )		{	glRecti(x1,y1,x2,y2);	};
 
@@ -197,4 +214,4 @@
 template< > inline void glMaterialv< Vector3r >	( GLenum face, GLenum pname, const Vector3r params )	{	const GLfloat _p[3]={(float) params[0], (float) params[1], (float) params[2]}; glMaterialfv(face,pname,_p);	};
 template< > inline void glMaterialv< Vector3i >		( GLenum face, GLenum pname, const Vector3i params )	{	glMaterialiv(face,pname,(int*)&params);	};
 
-
+#undef LDOUBL

=== modified file 'pkg/common/Facet.cpp'
--- pkg/common/Facet.cpp	2013-05-24 16:48:19 +0000
+++ pkg/common/Facet.cpp	2013-09-08 11:12:42 +0000
@@ -28,7 +28,6 @@
 	normal = e[0].cross(e[1]);
 	area = .5*normal.norm();
 	normal /= 2*area;
-	//normal.normalize();
 	for(int i=0; i<3; ++i){
 		ne[i]=e[i].cross(normal); ne[i].normalize();
 		vl[i]=vertices[i].norm();

=== modified file 'pkg/common/Facet.hpp'
--- pkg/common/Facet.hpp	2013-05-24 16:48:19 +0000
+++ pkg/common/Facet.hpp	2013-09-08 11:12:42 +0000
@@ -37,7 +37,7 @@
 
 	YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Facet,Shape,"Facet (triangular particle) geometry.",
 		((vector<Vector3r>,vertices,vector<Vector3r>(3,Vector3r(NaN,NaN,NaN)),(Attr::triggerPostLoad | Attr::noResize),"Vertex positions in local coordinates."))
-		((Vector3r,normal,Vector3r(NaN,NaN,NaN),(Attr::readonly | Attr::noSave),"Facet's normal"))
+		((Vector3r,normal,Vector3r(NaN,NaN,NaN),(Attr::readonly | Attr::noSave),"Facet's normal (in local coordinate system)"))
 		((Real,area,NaN,(Attr::readonly | Attr::noSave),"Facet's area"))
 		#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]"))

=== added file 'pkg/dem/Ip2_ElastMat.cpp'
--- pkg/dem/Ip2_ElastMat.cpp	1970-01-01 00:00:00 +0000
+++ pkg/dem/Ip2_ElastMat.cpp	2013-09-08 11:12:42 +0000
@@ -0,0 +1,64 @@
+#include "Ip2_ElastMat.hpp"
+
+#include <yade/pkg/common/NormShearPhys.hpp>
+#include <yade/pkg/common/NormShearPhys.hpp>
+#include <yade/pkg/dem/DemXDofGeom.hpp>
+
+YADE_PLUGIN((Ip2_ElastMat_ElastMat_NormPhys)(Ip2_ElastMat_ElastMat_NormShearPhys));
+
+
+void Ip2_ElastMat_ElastMat_NormPhys::go( const shared_ptr<Material>& b1
+					, const shared_ptr<Material>& b2
+					, const shared_ptr<Interaction>& interaction)
+{
+	if(interaction->phys) return;
+	const shared_ptr<ElastMat>& mat1 = YADE_PTR_CAST<ElastMat>(b1);
+	const shared_ptr<ElastMat>& mat2 = YADE_PTR_CAST<ElastMat>(b2);
+	Real Ea 	= mat1->young;
+	Real Eb 	= mat2->young;
+	interaction->phys = shared_ptr<NormPhys>(new NormPhys());
+	const shared_ptr<NormPhys>& phys = YADE_PTR_CAST<NormPhys>(interaction->phys);
+	Real Kn;
+	const GenericSpheresContact* geom=dynamic_cast<GenericSpheresContact*>(interaction->geom.get());
+	if (geom) {
+		Real Ra,Rb;//Vector3r normal;
+		Ra=geom->refR1>0?geom->refR1:geom->refR2;
+		Rb=geom->refR2>0?geom->refR2:geom->refR1;
+		//harmonic average of the two stiffnesses when (Ri.Ei/2) is the stiffness of a contact point on sphere "i"
+		Kn = 2*Ea*Ra*Eb*Rb/(Ea*Ra+Eb*Rb);
+	} else {
+		Kn = 2*Ea*Eb/(Ea+Eb);
+	}
+	phys->kn = Kn;
+};
+
+
+void Ip2_ElastMat_ElastMat_NormShearPhys::go( const shared_ptr<Material>& b1
+					, const shared_ptr<Material>& b2
+					, const shared_ptr<Interaction>& interaction)
+{
+	if(interaction->phys) return;
+	const shared_ptr<ElastMat>& mat1 = YADE_PTR_CAST<ElastMat>(b1);
+	const shared_ptr<ElastMat>& mat2 = YADE_PTR_CAST<ElastMat>(b2);
+	Real Ea 	= mat1->young;
+	Real Eb 	= mat2->young;
+	Real Va 	= mat1->poisson;
+	Real Vb 	= mat2->poisson;
+	interaction->phys = shared_ptr<NormShearPhys>(new NormShearPhys());
+	const shared_ptr<NormShearPhys>& phys = YADE_PTR_CAST<NormShearPhys>(interaction->phys);
+	Real Kn, Ks;
+	GenericSpheresContact* geom=dynamic_cast<GenericSpheresContact*>(interaction->geom.get());
+	if (geom) {
+		Real Ra,Rb;//Vector3r normal;
+		Ra=geom->refR1>0?geom->refR1:geom->refR2;
+		Rb=geom->refR2>0?geom->refR2:geom->refR1;
+		//harmonic average of the two stiffnesses when (Ri.Ei/2) is the stiffness of a contact point on sphere "i"
+		Kn = 2*Ea*Ra*Eb*Rb/(Ea*Ra+Eb*Rb);
+		Ks = 2*Ea*Ra*Va*Eb*Rb*Vb/(Ea*Ra*Va+Eb*Rb*Vb);
+	} else {
+		Kn = 2*Ea*Eb/(Ea+Eb);
+		Kn = 2*Ea*Va*Eb*Vb/(Ea*Va+Eb*Vb);
+	}
+	phys->kn = Kn;
+	phys->ks = Ks;
+};

=== added file 'pkg/dem/Ip2_ElastMat.hpp'
--- pkg/dem/Ip2_ElastMat.hpp	1970-01-01 00:00:00 +0000
+++ pkg/dem/Ip2_ElastMat.hpp	2013-09-08 11:12:42 +0000
@@ -0,0 +1,24 @@
+
+#include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/common/MatchMaker.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+
+class Ip2_ElastMat_ElastMat_NormPhys: public IPhysFunctor{
+	public:
+		virtual void go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction);
+	FUNCTOR2D(ElastMat,ElastMat);
+	YADE_CLASS_BASE_DOC_ATTRS(Ip2_ElastMat_ElastMat_NormPhys,IPhysFunctor,"Create a :yref:`NormPhys` from two :yref:`ElastMats<ElastMat>`. TODO. EXPERIMENTAL",
+	);
+};
+REGISTER_SERIALIZABLE(Ip2_ElastMat_ElastMat_NormPhys);
+
+
+class Ip2_ElastMat_ElastMat_NormShearPhys: public IPhysFunctor{
+	public:
+		virtual void go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction);
+	FUNCTOR2D(ElastMat,ElastMat);
+	YADE_CLASS_BASE_DOC_ATTRS(Ip2_ElastMat_ElastMat_NormShearPhys,IPhysFunctor,"Create a :yref:`NormShearPhys` from two :yref:`ElastMats<ElastMat>`. TODO. EXPERIMENTAL",
+	);
+};
+REGISTER_SERIALIZABLE(Ip2_ElastMat_ElastMat_NormShearPhys);
+

=== modified file 'pkg/dem/Shop.cpp'
--- pkg/dem/Shop.cpp	2013-06-20 14:56:35 +0000
+++ pkg/dem/Shop.cpp	2013-09-08 11:12:42 +0000
@@ -206,7 +206,8 @@
 			// the tensorial expression http://en.wikipedia.org/wiki/Moment_of_inertia#Moment_of_inertia_tensor
 			// inertia tensor rotation from http://www.kwon3d.com/theory/moi/triten.html
 			Matrix3r mI; mI<<state->inertia[0],0,0, 0,state->inertia[1],0, 0,0,state->inertia[2];
-			E+=.5*state->angVel.transpose().dot((T.transpose()*mI*T)*state->angVel);
+			//E+=.5*state->angVel.transpose().dot((T.transpose()*mI*T)*state->angVel);
+			E+=.5*state->angVel.transpose().dot((T*mI*T.transpose())*state->angVel);
 		}
 		else { E+=0.5*state->angVel.dot(state->inertia.cwiseProduct(state->angVel));}
 		if(maxId && E>maxE) { *maxId=b->getId(); maxE=E; }
@@ -215,6 +216,27 @@
 	return ret;
 }
 
+Vector3r Shop::momentum() {
+	Vector3r ret = Vector3r::Zero();
+	Scene* scene=Omega::instance().getScene().get();
+	FOREACH(const shared_ptr<Body> b, *scene->bodies){
+		ret += b->state->mass * b->state->vel;
+	}
+	return ret;
+}
+
+Vector3r Shop::angularMomentum(Vector3r origin) {
+	Vector3r ret = Vector3r::Zero();
+	Scene* scene=Omega::instance().getScene().get();
+	Matrix3r T,Iloc;
+	FOREACH(const shared_ptr<Body> b, *scene->bodies){
+		ret += (b->state->pos-origin).cross(b->state->mass * b->state->vel);
+		ret += b->state->angMom;
+	}
+	return ret;
+}
+
+
 shared_ptr<FrictMat> Shop::defaultGranularMat(){
 	shared_ptr<FrictMat> mat(new FrictMat);
 	mat->density=2e3;

=== modified file 'pkg/dem/Shop.hpp'
--- pkg/dem/Shop.hpp	2013-05-30 20:19:43 +0000
+++ pkg/dem/Shop.hpp	2013-09-08 11:12:42 +0000
@@ -84,6 +84,10 @@
 		//! Get unbalanced force of the whole simulation
 		static Real unbalancedForce(bool useMaxForce=false, Scene* _rb=NULL);
 		static Real kineticEnergy(Scene* _rb=NULL, Body::id_t* maxId=NULL);
+		//! get total momentum of current simulation
+		static Vector3r momentum();
+		//! get total angular momentum of current simulation
+		static Vector3r angularMomentum(Vector3r origin = Vector3r::Zero());
 
 		static Vector3r totalForceInVolume(Real& avgIsoStiffness, Scene *_rb=NULL);
 

=== modified file 'pkg/dem/Tetra.cpp'
--- pkg/dem/Tetra.cpp	2012-02-16 16:05:15 +0000
+++ pkg/dem/Tetra.cpp	2013-09-08 11:12:42 +0000
@@ -1,16 +1,23 @@
 // © 2007 Václav Šmilauer <eudoxos@xxxxxxxx>
+// © 2013 Jan Stránský <jan.stransky@xxxxxxxxxxx>
 
 #include"Tetra.hpp"
 
-YADE_PLUGIN(/* self-contained in hpp: */ (Tetra) (TTetraGeom) (Bo1_Tetra_Aabb) 
-	/* some code in cpp (this file): */ (TetraVolumetricLaw) (Ig2_Tetra_Tetra_TTetraGeom)
+YADE_PLUGIN(/* self-contained in hpp: */ (Tetra) (TTetraGeom) (TTetraSimpleGeom) (Bo1_Tetra_Aabb) 
+	/* some code in cpp (this file): */ (TetraVolumetricLaw) 
+	(Ig2_Tetra_Tetra_TTetraGeom)
+	#ifdef YADE_CGAL
+		(Ig2_Tetra_Tetra_TTetraSimpleGeom)
+	#endif
 	#ifdef YADE_OPENGL
 		(Gl1_Tetra)
 	#endif	
+	(Law2_TTetraSimpleGeom_NormPhys_Simple)
 	);
 
 Tetra::~Tetra(){}
 TTetraGeom::~TTetraGeom(){}
+TTetraSimpleGeom::~TTetraSimpleGeom(){}
 
 #include<yade/core/Interaction.hpp>
 #include<yade/core/Omega.hpp>
@@ -20,6 +27,11 @@
 
 #include<yade/pkg/common/Aabb.hpp>
 
+#ifdef YADE_CGAL
+	#include <CGAL/intersections.h>
+#endif
+
+
 void Bo1_Tetra_Aabb::go(const shared_ptr<Shape>& ig, shared_ptr<Bound>& bv, const Se3r& se3, const Body*){
 	Tetra* t=static_cast<Tetra*>(ig.get());
 	if(!bv){ bv=shared_ptr<Bound>(new Aabb); }
@@ -32,6 +44,569 @@
 	#undef __VOP
 }
 
+
+
+
+#ifdef YADE_CGAL
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::psMap[4][3] = { // segments of point
+	{0,2,3},
+	{0,1,4},
+	{1,2,5},
+	{3,4,5}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::ptMap[4][3] = { // triangles of point
+	{0,1,3},
+	{0,1,2},
+	{1,2,3},
+	{0,2,3}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::stMap[6][2] = { // triangles of segments
+	{0,1},
+	{1,2},
+	{1,3},
+	{0,3},
+	{0,2},
+	{2,3}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::ppsMap[4][4] = { // point-point pair to segment
+	{-1, 0, 2, 3},
+	{ 0,-1, 1, 4},
+	{ 2, 1,-1, 5},
+	{ 3, 4, 5,-1}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::tsMap[4][3] = { // segmnts of triangle
+	{0,3,4},
+	{0,1,2},
+	{1,4,5},
+	{2,3,5}
+};
+
+const int Ig2_Tetra_Tetra_TTetraSimpleGeom::sstMap[6][6] = { // segment-segment pair to triangle
+	{-1, 1, 1, 0, 0,-1},
+	{ 1,-1, 1,-1, 2, 2},
+	{ 1, 1,-1, 3,-1, 3},
+	{ 0,-1, 3,-1, 0, 3},
+	{ 0, 2,-1, 0,-1, 2},
+	{-1, 2, 3, 3, 2,-1}
+};
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkVertexToTriangleCase(
+														const Triangle tA[4],
+														const Point pB[4],
+														const Segment sB[6],
+														Vector3r& normal,
+														Vector3r& contactPoint,
+														Real& penetrationVolume)
+{
+	for (int i=0; i<4; i++) { // loop over triangles 1
+		const Triangle& t = tA[i]; // choose triangle 1
+		for (int j=0; j<4; j++) { // loop over vertices 2
+			const Point& p = pB[j]; // choose vertex 2
+			// choose edges posessing p
+			const Segment& sa = sB[psMap[j][0]];
+			const Segment& sb = sB[psMap[j][1]];
+			const Segment& sc = sB[psMap[j][2]];
+			if ( !(do_intersect(t,sa) && do_intersect(t,sb) && do_intersect(t,sc)) ) { continue; }// if all edges intersect with t
+				// evaluate the points
+			CGAL::Object oa = intersection(t,sa);
+			const Point* pa = CGAL::object_cast<Point>(&oa);
+			CGAL::Object ob = intersection(t,sb);
+			const Point* pb = CGAL::object_cast<Point>(&ob);
+			CGAL::Object oc = intersection(t,sc);
+			const Point* pc = CGAL::object_cast<Point>(&oc);
+			if ( !(pa && pb && pc) ) { continue; } // check that the intrsection really exists
+			Vector_3 n = CGAL::normal(t[0],t[1],t[2]); // normal of triangle
+			for (int k=0; k<3; k++) {
+				normal[k] = n[k]; // sets normal of contact = nornal of triangle
+				// contact point is center of mass of overlaping tetrahedron
+				contactPoint[k] = .25*(p[k]+pa->operator[](k)+pb->operator[](k)+pc->operator[](k));
+			}
+			normal.normalize();
+			const Point* v[4] = {&p,pa,pb,pc};
+			penetrationVolume = TetrahedronVolume(v);
+			Real vol = TetrahedronVolume(pB);
+			if (penetrationVolume > .5*vol) { penetrationVolume = vol-penetrationVolume; }
+			return true;
+		}
+	}
+	return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkEdgeToEdgeCase(
+														const Segment sA[6],
+														const Segment sB[6],
+														const Triangle tA[4],
+														const Triangle tB[4],
+														Vector3r& normal,
+														Vector3r& contactPoint,
+														Real& penetrationVolume)
+{
+	for (int i=0; i<6; i++) {
+		const Segment& sa = sA[i];
+		bool b[4];
+		const Triangle& ta0 = tA[stMap[i][0]];
+		const Triangle& ta1 = tA[stMap[i][1]];
+		for (int j=0; j<6; j++) {
+			const Segment sb = sB[j];
+			if ( !(do_intersect(sb,ta0) && do_intersect(sb,ta1) ) ) { continue; }
+			const Triangle tb0 = tB[stMap[j][0]];
+			const Triangle tb1 = tB[stMap[j][1]];
+			if ( !(do_intersect(sa,tb0) && do_intersect(sa,tb1) ) ) { continue; }
+			CGAL::Object osb1 = intersection(sb,ta0);
+			CGAL::Object osb2 = intersection(sb,ta1);
+			CGAL::Object osa1 = intersection(sa,tb0);
+			CGAL::Object osa2 = intersection(sa,tb1);
+			const Point* psb1 = CGAL::object_cast<Point>(&osb1);
+			const Point* psb2 = CGAL::object_cast<Point>(&osb2);
+			const Point* psa1 = CGAL::object_cast<Point>(&osa1);
+			const Point* psa2 = CGAL::object_cast<Point>(&osa2);
+			if ( !(psb1 && psb2 && psa1 && psa2) ) { continue; }
+			Vector_3 n = CGAL::cross_product(sa.to_vector(),sb.to_vector());
+			Vector3r nApprox;
+			for (int k=0; k<3; k++) {
+				normal[k] = n[k];
+				#define OP(p) p->operator[](k)
+				nApprox[k] = .5*(OP(psa1)+OP(psa2)) - .5*(OP(psb1)+OP(psb2));
+				contactPoint[k] = .25*(OP(psa1)+OP(psa2)+OP(psb1)+OP(psb2));
+				#undef OP
+			}
+			if ( nApprox.dot(normal) < 0 ) { normal *= (Real)-1.; }
+			normal.normalize();
+			const Point* p[4] = {psb1,psb2,psa1,psa2};
+			penetrationVolume = TetrahedronVolume(p);
+			return true;
+		}
+	}
+	return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkEdgeToTriangleCase1( // edge smaller than triangle
+														const Triangle tA[4],
+														const Segment sB[6],
+														const Point pB[6],
+														Vector3r& normal,
+														Vector3r& contactPoint,
+														Real& penetrationVolume)
+{
+	for (int i=0; i<4; i++) {
+		const Triangle& ta = tA[i];
+		int ni = 0;
+		for (int j=0; j<6; j++) {
+			const Segment& s = sB[j];
+			if ( do_intersect(ta,s) ) { ni++; }
+		}
+		if ( ni != 4 ) { continue; }
+		Vector_3 n = CGAL::normal(ta[0],ta[1],ta[2]);
+		for (int j=0; j<3; j++) {
+			const Point& p1 = pB[j];
+			if ( Vector_3(ta[0],p1)*n > 0.) { continue; }
+			const Segment& s10 = sB[psMap[j][0]];
+			const Segment& s11 = sB[psMap[j][1]];
+			const Segment& s12 = sB[psMap[j][2]];
+			bool b10 = do_intersect(ta,s10);
+			bool b11 = do_intersect(ta,s11);
+			bool b12 = do_intersect(ta,s12);
+			if ( !((b10 && b11) || (b11 && b12) || (b12 && b10) ) ) { continue; }
+			for (int k=j+1; k<4; k++) {
+				const Point& p2 = pB[k];
+				if ( Vector_3(ta[0],p2)*n > 0. ) { continue; }
+				const Segment& s20 = sB[psMap[k][0]];
+				const Segment& s21 = sB[psMap[k][1]];
+				const Segment& s22 = sB[psMap[k][2]];
+				bool b20 = do_intersect(ta,s20);
+				bool b21 = do_intersect(ta,s21);
+				bool b22 = do_intersect(ta,s22);
+				if ( !((b20 && b21) || (b21 && b22) || (b22 && b20) ) ) { continue; }
+				int si = ppsMap[j][k];
+				const Segment& sb = sB[si];
+				int l,m;
+				for (l=0; l<3; l++) {
+					if (l!=j && l!=k) { break; }
+				}
+				for (m=l+1; m<4; m++) {
+					if (m!=j && m!=k) { break; }
+				}
+				const Point& p3 = pB[l];
+				const Point& p4 = pB[m];
+				const Segment& s13 = sB[ppsMap[j][l]];
+				const Segment& s14 = sB[ppsMap[j][m]];
+				const Segment& s23 = sB[ppsMap[k][l]];
+				const Segment& s24 = sB[ppsMap[k][m]];
+				CGAL::Object o13 = intersection(ta,s13);
+				CGAL::Object o14 = intersection(ta,s14);
+				CGAL::Object o23 = intersection(ta,s23);
+				CGAL::Object o24 = intersection(ta,s24);
+				const Point* ps13 = CGAL::object_cast<Point>(&o13);
+				const Point* ps14 = CGAL::object_cast<Point>(&o14);
+				const Point* ps23 = CGAL::object_cast<Point>(&o23);
+				const Point* ps24 = CGAL::object_cast<Point>(&o24);
+				if ( !(ps13 && ps14 && ps23 && ps24) ) { continue; }
+				const Point* pp1[4] = {&p1,&p2,ps13,ps14};
+				const Point* pp2[4] = {&p2,ps23,ps24,ps14};
+				const Point* pp3[4] = {&p2,ps23,ps13,ps14};
+				Real v1 = TetrahedronVolume(pp1);
+				Real v2 = TetrahedronVolume(pp2);
+				Real v3 = TetrahedronVolume(pp3);
+				Vector3r cg1,cg2,cg3;
+				for (l=0; l<3; l++) {
+					normal[l] = n[l];
+					#define OP(p) p->operator[](l)
+					cg1[l] = .25*(p1[l]+p2[l]+OP(ps13)+OP(ps14));
+					cg2[l] = .25*(p2[l]+OP(ps23)+OP(ps24)+OP(ps14));
+					cg3[l] = .25*(p2[l]+OP(ps23)+OP(ps13)+OP(ps14));
+					#undef OP
+				}
+				penetrationVolume = v1 + v2 + v3;
+				contactPoint = (v1*cg1 + v2*cg2 + v3*cg3) / penetrationVolume;
+				normal.normalize();
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkEdgeToTriangleCase2( // edge larger than triangle
+														const Triangle tA[4],
+														const Triangle tB[4],
+														const Segment sA[6],
+														const Segment sB[6],
+														Vector3r& normal,
+														Vector3r& contactPoint,
+														Real& penetrationVolume)
+{
+
+	for (int i=0; i<6; i++) {
+		const Segment& sb = sB[i];
+		int ni = 0;
+		for (int j=0; j<4; j++) {
+			const Triangle& t = tA[j];
+			if ( do_intersect(t,sb) ) { ni++; }
+		}
+		if ( ni != 2 ) { continue; }
+		for (int j=0; j<3; j++) {
+			const Triangle& ta1 = tA[j];
+			if ( !(do_intersect(ta1,sb) ) ) { continue; }
+			for (int k=j+1; k<4; k++) {
+				const Triangle& ta2 = tA[k];
+				if ( !(do_intersect(ta2,sb) ) ) { continue; }
+				const Triangle& tb1 = tB[stMap[i][0]];
+				const Triangle& tb2 = tB[stMap[i][1]];
+				const Segment& sa1a = sA[tsMap[j][0]];
+				const Segment& sa1b = sA[tsMap[j][1]];
+				const Segment& sa1c = sA[tsMap[j][2]];
+				bool b1a = do_intersect(sa1a,tb1) && do_intersect(sa1a,tb2);
+				bool b1b = do_intersect(sa1b,tb1) && do_intersect(sa1b,tb2);
+				bool b1c = do_intersect(sa1c,tb1) && do_intersect(sa1c,tb2);
+				if ( !(b1a || b1b || b1c) ) { continue; }
+				const Segment& sa2a = sA[tsMap[k][0]];
+				const Segment& sa2b = sA[tsMap[k][1]];
+				const Segment& sa2c = sA[tsMap[k][2]];
+				bool b2a = do_intersect(sa2a,tb1) && do_intersect(sa2a,tb2);
+				bool b2b = do_intersect(sa2b,tb1) && do_intersect(sa2b,tb2);
+				bool b2c = do_intersect(sa2c,tb1) && do_intersect(sa2c,tb2);
+				if ( !(b2a || b2b || b2c) ) { continue; } 
+				int l = b1a? tsMap[j][0] : b1b? tsMap[j][1] : tsMap[j][2];
+				int m = b2a? tsMap[k][0] : b2b? tsMap[k][1] : tsMap[k][2];
+				const Segment& sa1 = sA[l];
+				const Segment& sa2 = sA[m];
+				const Triangle& taN = tA[sstMap[l][m]];
+				CGAL::Object o1  = intersection(sb,ta1);
+				CGAL::Object o2  = intersection(sb,ta2);
+				CGAL::Object o11 = intersection(sa1,tb1);
+				CGAL::Object o12 = intersection(sa1,tb2);
+				CGAL::Object o21 = intersection(sa2,tb1);
+				CGAL::Object o22 = intersection(sa2,tb2);
+				const Point* p1  = CGAL::object_cast<Point>(&o1);
+				const Point* p2  = CGAL::object_cast<Point>(&o2);
+				const Point* p11 = CGAL::object_cast<Point>(&o11);
+				const Point* p12 = CGAL::object_cast<Point>(&o12);
+				const Point* p21 = CGAL::object_cast<Point>(&o21);
+				const Point* p22 = CGAL::object_cast<Point>(&o22);
+				if ( !(p1 && p2 && p11 && p12 && p21 && p22) ) { continue; }
+				const Point* pp1[4] = {p1,p2,p11,p12};
+				const Point* pp2[4] = {p2,p21,p22,p12};
+				const Point* pp3[4] = {p2,p21,p11,p12};
+				Real v1 = TetrahedronVolume(pp1);
+				Real v2 = TetrahedronVolume(pp2);
+				Real v3 = TetrahedronVolume(pp3);
+				Vector3r cg1,cg2,cg3;
+				Vector_3 n = CGAL::normal(taN[0],taN[1],taN[2]);
+				for (int l=0; l<3; l++) {
+					normal[l] = n[l];
+					#define OP(p) p->operator[](l)
+					cg1[l] = .25*(OP(p1)+OP(p2)+OP(p11)+OP(p12));
+					cg2[l] = .25*(OP(p2)+OP(p21)+OP(p22)+OP(p12));
+					cg3[l] = .25*(OP(p2)+OP(p21)+OP(p11)+OP(p12));
+					#undef OP
+				}
+				penetrationVolume = v1 + v2 + v3;
+				contactPoint = (v1*cg1 + v2*cg2 + v3*cg3) / penetrationVolume;
+				normal.normalize();
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkVertexToEdgeCase(
+														const Point pA[4],
+														const Segment sA[6],
+														const Triangle tA[4],
+														const Segment sB[6],
+														const Triangle tB[4],
+														Vector3r& normal,
+														Vector3r& contactPoint,
+														Real& penetrationVolume)
+{
+	for (int i=0; i<4; i++) {
+		const Point& pa = pA[i];
+		if ( Vector_3(tB[0][0],pa)*CGAL::normal(tB[0][0],tB[0][1],tB[0][2]) > 0. ) { continue; }
+		if ( Vector_3(tB[1][0],pa)*CGAL::normal(tB[1][0],tB[1][1],tB[1][2]) > 0. ) { continue; }
+		if ( Vector_3(tB[2][0],pa)*CGAL::normal(tB[2][0],tB[2][1],tB[2][2]) > 0. ) { continue; }
+		if ( Vector_3(tB[3][0],pa)*CGAL::normal(tB[3][0],tB[3][1],tB[3][2]) > 0. ) { continue; }
+		const Segment& sa1 = sA[psMap[i][0]];
+		const Segment& sa2 = sA[psMap[i][1]];
+		const Segment& sa3 = sA[psMap[i][2]];
+		for (int j=0; j<6; j++) {
+			const Segment& sb = sB[j];
+			const Triangle& tb1 = tB[stMap[j][0]];
+			const Triangle& tb2 = tB[stMap[j][1]];
+			const Triangle& ta1 = tA[ptMap[i][0]];
+			const Triangle& ta2 = tA[ptMap[i][1]];
+			const Triangle& ta3 = tA[ptMap[i][2]];
+			bool bsa1tb1 = do_intersect(sa1,tb1);
+			bool bsa1tb2 = do_intersect(sa1,tb2);
+			bool bsa2tb1 = do_intersect(sa2,tb1);
+			bool bsa2tb2 = do_intersect(sa2,tb2);
+			bool bsa3tb1 = do_intersect(sa3,tb1);
+			bool bsa3tb2 = do_intersect(sa3,tb2);
+			bool bsbta1 = do_intersect(sb,ta1);
+			bool bsbta2 = do_intersect(sb,ta2);
+			bool bsbta3 = do_intersect(sb,ta3);
+			if ( !( (bsa1tb1 || bsa1tb2) && (bsa2tb1 || bsa2tb2) && (bsa3tb1 || bsa3tb2) && ((bsbta1 && bsbta2) || (bsbta2 && bsbta3) || (bsbta3 && bsbta1)) ) ) { continue; }
+			CGAL::Object oa1 = intersection(sa1,bsa1tb1? tb1 : tb2);
+			CGAL::Object oa2 = intersection(sa2,bsa2tb1? tb1 : tb2);
+			CGAL::Object oa3 = intersection(sa3,bsa3tb1? tb1 : tb2);
+			CGAL::Object ob1 = intersection(sb, (bsbta1 && bsbta2)? ta1 : (bsbta2 && bsbta3)? ta2 : ta3);
+			CGAL::Object ob2 = intersection(sb, (bsbta1 && bsbta2)? ta2 : (bsbta2 && bsbta3)? ta3 : ta1);
+			const Point* pa1 = CGAL::object_cast<Point>(&oa1);
+			const Point* pa2 = CGAL::object_cast<Point>(&oa2);
+			const Point* pa3 = CGAL::object_cast<Point>(&oa3);
+			const Point* pb1 = CGAL::object_cast<Point>(&ob1);
+			const Point* pb2 = CGAL::object_cast<Point>(&ob2);
+			if ( !(pa1 && pa2 && pa3 && pb1 && pb2) ) { continue; }
+			Segment sa(*pa1,*pa2);
+			Real d1 = sqrt(CGAL::squared_distance(sa,*pb1));
+			Real d2 = sqrt(CGAL::squared_distance(sa,*pb2));
+			const Point* ppb1 = d1<d2? pb1 : pb2;
+			const Point* ppb2 = d1<d2? pb2 : pb1;
+			const Point* pp1[4] = {&pa,pa1,pa2,pa3};
+			const Point* pp2[4] = {pa1,pa2,pa3,ppb2};
+			const Point* pp3[4] = {pa1,pa2,ppb1,ppb2};
+			Real v1 = TetrahedronVolume(pp1);
+			Real v2 = TetrahedronVolume(pp2);
+			Real v3 = TetrahedronVolume(pp3);
+			Vector3r cg1,cg2,cg3;
+			Vector_3 n(pa,sb.supporting_line().projection(pa));
+			for (int l=0; l<3; l++) {
+				normal[l] = n[l];
+				#define OP(p) p->operator[](l)
+				cg1[l] = .25*(pa[l]+OP(pa1)+OP(pa2)+OP(pa3));
+				cg2[l] = .25*(OP(pa1)+OP(pa2)+OP(pa3)+OP(ppb2));
+				cg3[l] = .25*(OP(pa1)+OP(pa2)+OP(ppb1)+OP(ppb2));
+				#undef OP
+			}
+			penetrationVolume = v1 + v2 + v3;
+			contactPoint = (v1*cg1 + v2*cg2 + v3*cg3) / penetrationVolume;
+			normal.normalize();
+			return true;
+		}
+	}
+	return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::checkVertexToVertexCase(
+														const Point pA[4],
+														const Point pB[4],
+														const Segment sA[6],
+														const Triangle tA[4],
+														const Triangle tB[4],
+														Vector3r& normal,
+														Vector3r& contactPoint,
+														Real& penetrationVolume)
+{
+	for (int i=0; i<4; i++) {
+		const Point& pa = pA[i];
+		if ( Vector_3(tB[0][0],pa)*CGAL::normal(tB[0][0],tB[0][1],tB[0][2]) > 0. ) { continue; }
+		if ( Vector_3(tB[1][0],pa)*CGAL::normal(tB[1][0],tB[1][1],tB[1][2]) > 0. ) { continue; }
+		if ( Vector_3(tB[2][0],pa)*CGAL::normal(tB[2][0],tB[2][1],tB[2][2]) > 0. ) { continue; }
+		if ( Vector_3(tB[3][0],pa)*CGAL::normal(tB[3][0],tB[3][1],tB[3][2]) > 0. ) { continue; }
+		const Segment& sa1 = sA[psMap[i][0]];
+		const Segment& sa2 = sA[psMap[i][1]];
+		const Segment& sa3 = sA[psMap[i][2]];
+		for (int j=0; j<4; j++) {
+			const Point& pb = pB[j];
+			if ( Vector_3(tA[0][0],pb)*CGAL::normal(tA[0][0],tA[0][1],tA[0][2]) > 0. ) { continue; }
+			if ( Vector_3(tA[1][0],pb)*CGAL::normal(tA[1][0],tA[1][1],tA[1][2]) > 0. ) { continue; }
+			if ( Vector_3(tA[2][0],pb)*CGAL::normal(tA[2][0],tA[2][1],tA[2][2]) > 0. ) { continue; }
+			if ( Vector_3(tA[3][0],pb)*CGAL::normal(tA[3][0],tB[3][1],tB[3][2]) > 0. ) { continue; }
+			const Triangle& tb1 = tB[ptMap[j][0]];
+			const Triangle& tb2 = tB[ptMap[j][1]];
+			const Triangle& tb3 = tB[ptMap[j][2]];
+			bool b11 = do_intersect(sa1,tb1);
+			bool b12 = do_intersect(sa1,tb2);
+			bool b13 = do_intersect(sa1,tb3);
+			bool b21 = do_intersect(sa2,tb1);
+			bool b22 = do_intersect(sa2,tb2);
+			bool b23 = do_intersect(sa2,tb3);
+			bool b31 = do_intersect(sa3,tb1);
+			bool b32 = do_intersect(sa3,tb2);
+			bool b33 = do_intersect(sa3,tb3);
+			if ( !(b11 || b12 || b13) && (b21 || b22 || b23) && (b31 || b32 || b33) ) { continue; }
+			CGAL::Object o1 = intersection(sa1, b11? tb1: b12? tb2 : tb3);
+			CGAL::Object o2 = intersection(sa2, b21? tb1: b22? tb2 : tb3);
+			CGAL::Object o3 = intersection(sa3, b31? tb1: b32? tb2 : tb3);
+			const Point* p1 = CGAL::object_cast<Point>(&o1);
+			const Point* p2 = CGAL::object_cast<Point>(&o2);
+			const Point* p3 = CGAL::object_cast<Point>(&o3);
+			if ( !(p1 && p2 && p3) ) { continue; }
+			const Point* pp1[4] = {&pa,p1,p2,p3};
+			const Point* pp2[4] = {&pb,p2,p3,p3};
+			Real v1 = TetrahedronVolume(pp1);
+			Real v2 = TetrahedronVolume(pp2);
+			Vector3r cg1,cg2;
+			Vector_3 n(pa,pb);
+			for (int l=0; l<3; l++) {
+				normal[l] = n[l];
+				#define OP(p) p->operator[](l)
+				cg1[l] = .25*(pa[l]+OP(p1)+OP(p2)+OP(p3));
+				cg2[l] = .25*(pb[l]+OP(p1)+OP(p2)+OP(p3));
+				#undef OP
+			}
+			penetrationVolume = v1 + v2;
+			contactPoint = (v1*cg1 + v2*cg2) / penetrationVolume;
+			normal.normalize();
+			return true;
+		}
+	}
+	return false;
+}
+
+bool Ig2_Tetra_Tetra_TTetraSimpleGeom::go(
+														const shared_ptr<Shape>& cm1,
+														const shared_ptr<Shape>& cm2,
+														const State& state1,
+														const State& state2,
+														const Vector3r& shift2,
+														const bool& force,
+														const shared_ptr<Interaction>& interaction)
+{
+	const Se3r& se31=state1.se3; const Se3r& se32=state2.se3;
+	Tetra* shape1 = static_cast<Tetra*>(cm1.get());
+	Tetra* shape2 = static_cast<Tetra*>(cm2.get());
+
+	Point p1[4], p2[4];
+	Vector3r vTemp;
+	// vertices in global coordinates
+	for (int i=0; i<4; i++) {
+		vTemp = se31.position + se31.orientation*shape1->v[i];
+		p1[i] = Point(vTemp[0],vTemp[1],vTemp[2]);
+		vTemp = se32.position + se32.orientation*shape2->v[i];
+		p2[i] = Point(vTemp[0],vTemp[1],vTemp[2]);
+	}
+
+	// Faces (CGAL triangles) of each tetra
+	#define T(p,i,j,k) Triangle(p[i],p[j],p[k])
+	const Triangle t1[4] = {T(p1,0,1,3),T(p1,0,2,1),T(p1,1,2,3),T(p1,0,3,2)};
+	const Triangle t2[4] = {T(p2,0,1,3),T(p2,0,2,1),T(p2,1,2,3),T(p2,0,3,2)};
+	#undef T
+	// Edges (CGAL segments) of each tetra
+	#define S(p,i,j) Segment(p[i],p[j])
+	const Segment s1[6] = {S(p1,0,1),S(p1,1,2),S(p1,0,2),S(p1,0,3),S(p1,1,3),S(p1,2,3)};
+	const Segment s2[6] = {S(p2,0,1),S(p2,1,2),S(p2,0,2),S(p2,0,3),S(p2,1,3),S(p2,2,3)};
+	#undef S
+
+	Vector3r n;
+	Vector3r cp;
+	Real V;
+	int flag;
+
+	# define SET_GEOM_AND_RETURN_TRUE \
+		shared_ptr<TTetraSimpleGeom> geom; \
+		if (!interaction->geom) geom=shared_ptr<TTetraSimpleGeom>(new TTetraSimpleGeom()); \
+		else geom=YADE_PTR_CAST<TTetraSimpleGeom>(interaction->geom); \
+		interaction->geom=geom; \
+		geom->normal = n; \
+		geom->contactPoint = cp; \
+		geom->penetrationVolume = V; \
+		geom->flag = flag; \
+		return true;
+
+	if (checkVertexToTriangleCase(t1,p2,s2,n,cp,V)) {
+		flag = 1;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkVertexToTriangleCase(t2,p1,s1,n,cp,V)) {
+		n *= -1.;
+		flag = 2;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkEdgeToEdgeCase(s1,s2,t1,t2,n,cp,V)) {
+		flag = 3;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkEdgeToTriangleCase1(t1,s2,p2,n,cp,V)) {
+		flag = 4;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkEdgeToTriangleCase1(t2,s1,p1,n,cp,V)) {
+		n *= -1.;
+		flag = 5;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkEdgeToTriangleCase2(t1,t2,s1,s2,n,cp,V)) {
+		flag = 6;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkEdgeToTriangleCase2(t2,t1,s2,s1,n,cp,V)) {
+		n *= -1.;
+		flag = 7;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkVertexToEdgeCase(p1,s1,t1,s2,t2,n,cp,V)) {
+		n *= -1.;
+		flag = 8;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+	if (checkVertexToEdgeCase(p2,s2,t2,s1,t1,n,cp,V)) {
+		flag = 9;
+		SET_GEOM_AND_RETURN_TRUE
+	}
+
+	#undef SET_GEOM_AND_RETURN_TRUE
+	
+	if (interaction->geom) {
+		TTetraSimpleGeom* geom = static_cast<TTetraSimpleGeom*>(interaction->geom.get());
+		geom->penetrationVolume = (Real)-1.;
+		geom->flag = 0;
+		return true;
+	}
+	return false;
+}
+#endif
+
+
+
+
+
+
+
 CREATE_LOGGER(Ig2_Tetra_Tetra_TTetraGeom);
 
 /*! Calculate configuration of Tetra - Tetra intersection.
@@ -107,7 +682,9 @@
 		tA=Tetra(Vector3r(0,0,0),Vector3r(1,0,0),Vector3r(0,1,0),Vector3r(0,0,1));
 	#endif
 	list<Tetra> tAB=Tetra2TetraIntersection(tA,tB);
-	if(tAB.size()==0) { /* LOG_DEBUG("No intersection."); */ return false;} //no intersecting volume
+	if (!interaction->isReal() && !force) {
+		if(tAB.size()==0) { /* LOG_DEBUG("No intersection."); */ return false;} //no intersecting volume
+	}
 
 	Real V(0); // volume of intersection (cummulative)
 	Vector3r Sg(0,0,0); // static moment of intersection
@@ -186,6 +763,8 @@
 	Real maxPenetrationDepthB=sqrt(6*(IB(ix,ix)+IB(ixx,ixx)-IB(ixxx,ixxx))/V);
 	TRVAR2(maxPenetrationDepthA,maxPenetrationDepthB);
 
+	//normal = se32.position - se31.position; normal.normalize();
+
 	/* store calculated stuff in bang; some is redundant */
 	bang->normal=normal;
 	bang->equivalentCrossSection=equivalentCrossSection;
@@ -367,6 +946,7 @@
 	return(ret); // prevent warning
 }
 
+
 CREATE_LOGGER(TetraVolumetricLaw);
 
 /*! Apply forces on tetrahedra in collision based on geometric configuration provided by Ig2_Tetra_Tetra_TTetraGeom.
@@ -413,12 +993,13 @@
 
 #ifdef YADE_OPENGL
 	#include<yade/lib/opengl/OpenGLWrapper.hpp>
-	void Gl1_Tetra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool,const GLViewInfo&)
+	bool Gl1_Tetra::wire;
+	void Gl1_Tetra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool wire2,const GLViewInfo&)
 	{
 		glMaterialv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,Vector3r(cm->color[0],cm->color[1],cm->color[2]));
 		glColor3v(cm->color);
 		Tetra* t=static_cast<Tetra*>(cm.get());
-		if (0) { // wireframe, as for Tetrahedron
+		if (wire && wire2) { // wireframe, as for Tetrahedron
 			glDisable(GL_LIGHTING);
 			glBegin(GL_LINES);
 				#define __ONEWIRE(a,b) glVertex3v(t->v[a]);glVertex3v(t->v[b])
@@ -432,10 +1013,10 @@
 			glDisable(GL_CULL_FACE); glEnable(GL_LIGHTING);
 			glBegin(GL_TRIANGLES);
 				#define __ONEFACE(a,b,c) n=(t->v[b]-t->v[a]).cross(t->v[c]-t->v[a]); n.normalize(); faceCenter=(t->v[a]+t->v[b]+t->v[c])/3.; if((faceCenter-center).dot(n)<0)n=-n; glNormal3v(n); glVertex3v(t->v[a]); glVertex3v(t->v[b]); glVertex3v(t->v[c]);
-					__ONEFACE(3,0,1);
-					__ONEFACE(0,1,2);
+					__ONEFACE(0,2,1);
+					__ONEFACE(0,1,3);
 					__ONEFACE(1,2,3);
-					__ONEFACE(2,3,0);
+					__ONEFACE(0,3,2);
 				#undef __ONEFACE
 			glEnd();
 		}
@@ -599,6 +1180,47 @@
 }
 
 
-Real TetrahedronVolume(const Vector3r v[4]) { return fabs((Vector3r(v[3])-Vector3r(v[0])).dot((Vector3r(v[3])-Vector3r(v[1])).cross(Vector3r(v[3])-Vector3r(v[2]))))/6.; }
-Real TetrahedronVolume(const vector<Vector3r>& v) { return fabs(Vector3r(v[1]-v[0]).dot(Vector3r(v[2]-v[0]).cross(v[3]-v[0])))/6.; }
+
+
+Real TetrahedronSignedVolume(const Vector3r v[4]) { return (Vector3r(v[3])-Vector3r(v[0])).dot((Vector3r(v[3])-Vector3r(v[1])).cross(Vector3r(v[3])-Vector3r(v[2])))/6.; }
+Real TetrahedronVolume(const Vector3r v[4]) { return fabs(TetrahedronSignedVolume(v)); }
+Real TetrahedronSignedVolume(const vector<Vector3r>& v) { return Vector3r(v[1]-v[0]).dot(Vector3r(v[2]-v[0]).cross(v[3]-v[0]))/6.; }
+Real TetrahedronVolume(const vector<Vector3r>& v) { return fabs(TetrahedronSignedVolume(v)); }
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> >* v[4]) {
+	Vector3r vv[4];
+	for (int i=0; i<4; i++) {
+		for (int j=0; j<3; j++) {
+			vv[i][j] = v[i]->operator[](j);
+		}
+	}
+	return TetrahedronVolume(vv);
+}
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> > v[4]) {
+	Vector3r vv[4];
+	for (int i=0; i<4; i++) {
+		for (int j=0; j<3; j++) {
+			vv[i][j] = v[i][j];
+		}
+	}
+	return TetrahedronVolume(vv);
+}
+
+
+
+void Law2_TTetraSimpleGeom_NormPhys_Simple::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* contact){
+	int id1 = contact->getId1(), id2 = contact->getId2();
+	TTetraSimpleGeom* geom= static_cast<TTetraSimpleGeom*>(ig.get());
+	NormPhys* phys = static_cast<NormPhys*>(ip.get());
+	if ( geom->flag == 0 || geom->penetrationVolume <= 0. ) {
+		scene->interactions->requestErase(contact);
+		return;
+	}
+	Real& un=geom->penetrationVolume;
+	phys->normalForce=phys->kn*std::max(un,(Real) 0)*geom->normal;
+
+	State* de1 = Body::byId(id1,scene)->state.get();
+	State* de2 = Body::byId(id2,scene)->state.get();
+	applyForceAtContactPoint(-phys->normalForce, geom->contactPoint, id1, de1->se3.position, id2, de2->se3.position);
+	// TODO periodic
+}
 

=== modified file 'pkg/dem/Tetra.hpp'
--- pkg/dem/Tetra.hpp	2011-01-09 16:34:50 +0000
+++ pkg/dem/Tetra.hpp	2013-09-08 11:12:42 +0000
@@ -1,4 +1,5 @@
 // © 2007 Václav Šmilauer <eudoxos@xxxxxxxx>
+// © 2013 Jan Stránský <jan.stransky@xxxxxxxxxxx>
 
 #pragma once
 
@@ -10,6 +11,12 @@
 
 #include<yade/pkg/common/Aabb.hpp>
 #include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/common/NormShearPhys.hpp>
+
+#ifdef YADE_CGAL
+	#include <CGAL/Cartesian.h>
+	//#include <CGAL/intersections.h>
+#endif
 
 
 /* Our mold of tetrahedron: just 4 vertices.
@@ -19,16 +26,16 @@
 	public:
 		Tetra(Vector3r v0, Vector3r v1, Vector3r v2, Vector3r v3) { createIndex(); v.resize(4); v[0]=v0; v[1]=v1; v[2]=v2; v[3]=v3; } 
 		virtual ~Tetra();
-	protected:
-		YADE_CLASS_BASE_DOC_ATTRS_CTOR(Tetra,Shape,"Tetrahedron geometry.",
-			((std::vector<Vector3r>,v,std::vector<Vector3r>(4),,"Tetrahedron vertices in global coordinate system.")),
-			/*ctor*/createIndex();
-		);
-		REGISTER_CLASS_INDEX(Tetra,Shape);
+	YADE_CLASS_BASE_DOC_ATTRS_CTOR(Tetra,Shape,"Tetrahedron geometry.",
+		((std::vector<Vector3r>,v,std::vector<Vector3r>(4),,"Tetrahedron vertices (in local coordinate system).")),
+		/*ctor*/createIndex();
+	);
+	REGISTER_CLASS_INDEX(Tetra,Shape);
 };
 REGISTER_SERIALIZABLE(Tetra);
 
 
+
 /*! Collision configuration for Tetra and something.
  * This is expressed as penetration volume properties: centroid, volume, orientation of principal axes, inertia.
  *
@@ -36,22 +43,37 @@
 class TTetraGeom: public IGeom{
 	public:
 		virtual ~TTetraGeom();
-	protected:
-		YADE_CLASS_BASE_DOC_ATTRS_CTOR(TTetraGeom,IGeom,"Geometry of interaction between 2 :yref:`tetrahedra<Tetra>`, including volumetric characteristics",
-			((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
-			((Real,equivalentCrossSection,NaN,,"Cross-section of the overlap (perpendicular to the axis of least inertia"))
-			((Real,maxPenetrationDepthA,NaN,,"??"))
-			((Real,maxPenetrationDepthB,NaN,,"??"))
-			((Real,equivalentPenetrationDepth,NaN,,"??"))
-			((Vector3r,contactPoint,,,"Contact point (global coords)"))
-			((Vector3r,normal,,,"Normal of the interaction, directed in the sense of least inertia of the overlap volume")),
-			createIndex();
-		);
-		//FUNCTOR2D(Tetra,Tetra);
-		REGISTER_CLASS_INDEX(TTetraGeom,IGeom);
+	YADE_CLASS_BASE_DOC_ATTRS_CTOR(TTetraGeom,IGeom,"Geometry of interaction between 2 :yref:`tetrahedra<Tetra>`, including volumetric characteristics",
+		((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
+		((Real,equivalentCrossSection,NaN,,"Cross-section of the overlap (perpendicular to the axis of least inertia"))
+		((Real,maxPenetrationDepthA,NaN,,"??"))
+		((Real,maxPenetrationDepthB,NaN,,"??"))
+		((Real,equivalentPenetrationDepth,NaN,,"??"))
+		((Vector3r,contactPoint,,,"Contact point (global coords)"))
+		((Vector3r,normal,,,"Normal of the interaction, directed in the sense of least inertia of the overlap volume")),
+		createIndex();
+	);
+	REGISTER_CLASS_INDEX(TTetraGeom,IGeom);
 };
 REGISTER_SERIALIZABLE(TTetraGeom);
 
+
+class TTetraSimpleGeom: public IGeom{
+	public:
+		virtual ~TTetraSimpleGeom();
+	YADE_CLASS_BASE_DOC_ATTRS_CTOR(TTetraSimpleGeom,IGeom,"EXPERIMENTAL. Geometry of interaction between 2 :yref:`tetrahedra<Tetra>`",
+		((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
+		((Vector3r,contactPoint,,,"Contact point (global coords)"))
+		((Vector3r,normal,,,"Normal of the interaction TODO"))
+		((int,flag,0,,"TODO")),
+		createIndex();
+	);
+	REGISTER_CLASS_INDEX(TTetraSimpleGeom,IGeom);
+};
+REGISTER_SERIALIZABLE(TTetraSimpleGeom);
+
+
+
 /*! Creates Aabb from Tetra. 
  *
  * Self-contained. */
@@ -69,7 +91,9 @@
 	class Gl1_Tetra: public GlShapeFunctor{	
 		public:
 			virtual void go(const shared_ptr<Shape>&, const shared_ptr<State>&,bool,const GLViewInfo&);
-		YADE_CLASS_BASE_DOC(Gl1_Tetra,GlShapeFunctor,"Renders :yref:`Tetra` object");
+		YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_Tetra,GlShapeFunctor,"Renders :yref:`Tetra` object",
+			((bool,wire,true,,"TODO"))
+		);
 		RENDERS(Tetra);
 	};
 	REGISTER_SERIALIZABLE(Gl1_Tetra);
@@ -108,11 +132,67 @@
 
 REGISTER_SERIALIZABLE(Ig2_Tetra_Tetra_TTetraGeom);
 
+
+
+#ifdef YADE_CGAL
+class Ig2_Tetra_Tetra_TTetraSimpleGeom: public IGeomFunctor
+{
+	protected:
+		typedef CGAL::Cartesian<Real> K;
+		typedef K::Point_3 Point;
+		typedef CGAL::Tetrahedron_3<K> CGALTetra;
+		typedef CGAL::Segment_3<K> Segment;
+		typedef CGAL::Triangle_3<K> Triangle;
+		typedef CGAL::Vector_3<K> Vector_3;
+		bool checkVertexToTriangleCase(const Triangle tA[4], const Point pB[4], const Segment sB[6], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+		bool checkEdgeToEdgeCase(const Segment sA[6], const Segment sB[6], const Triangle tA[4], const Triangle tB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+		bool checkEdgeToTriangleCase1(const Triangle tA[4], const Segment sB[6], const Point pB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+		bool checkEdgeToTriangleCase2(const Triangle tA[4], const Triangle tB[4], const Segment sA[6], const Segment sB[6], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+		bool checkVertexToEdgeCase(const Point pA[4], const Segment sA[6], const Triangle tA[4], const Segment sB[6], const Triangle tB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+		bool checkVertexToVertexCase(const Point pA[4], const Point pB[4], const Segment sA[6], const Triangle tA[4], const Triangle tB[4], Vector3r& normal, Vector3r& contactPoint, Real& penetrationVolume);
+		static const int psMap[4][3];
+		static const int ptMap[4][3];
+		static const int stMap[6][2];
+		static const int tsMap[4][3];
+		static const int ppsMap[4][4];
+		static const int sstMap[6][6];
+	public:
+		virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+		virtual bool goReverse(	const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c){ throw std::logic_error("Ig2_Tetra_Tetra_TTetraSimpleGeom::goReverse called, but the functor is symmetric."); }
+	FUNCTOR2D(Tetra,Tetra);
+	DEFINE_FUNCTOR_ORDER_2D(Tetra,Tetra);
+	YADE_CLASS_BASE_DOC(Ig2_Tetra_Tetra_TTetraSimpleGeom,IGeomFunctor,"EXPERIMANTAL. Create/update geometry of collision between 2 :yref:`tetrahedra<Tetra>` (:yref:`TTetraSimpleGeom` instance)");
+	DECLARE_LOGGER;
+};
+REGISTER_SERIALIZABLE(Ig2_Tetra_Tetra_TTetraSimpleGeom);
+#endif
+
+
+
+
+class Law2_TTetraSimpleGeom_NormPhys_Simple: public LawFunctor{
+	public:
+		virtual void go(shared_ptr<IGeom>& _geom, shared_ptr<IPhys>& _phys, Interaction* I);
+	YADE_CLASS_BASE_DOC(Law2_TTetraSimpleGeom_NormPhys_Simple,LawFunctor,"EXPERIMENTAL. TODO");
+	FUNCTOR2D(TTetraSimpleGeom,NormPhys);
+	DECLARE_LOGGER;
+};
+REGISTER_SERIALIZABLE(Law2_TTetraSimpleGeom_NormPhys_Simple);
+
+
+
+
 // Miscillaneous functions
 //! Tetrahedron's volume.
 /// http://en.wikipedia.org/wiki/Tetrahedron#Surface_area_and_volume
+Real TetrahedronSignedVolume(const Vector3r v[4]);
 Real TetrahedronVolume(const Vector3r v[4]);
+Real TetrahedronSignedVolume(const vector<Vector3r>& v);
 Real TetrahedronVolume(const vector<Vector3r>& v);
+#ifdef YADE_CGAL
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> >* v[4]);
+Real TetrahedronVolume(const CGAL::Point_3<CGAL::Cartesian<Real> > v[4]);
+#endif
 Matrix3r TetrahedronInertiaTensor(const vector<Vector3r>& v);
 //Matrix3r TetrahedronInertiaTensor(const Vector3r v[4]);
 Matrix3r TetrahedronCentralInertiaTensor(const vector<Vector3r>& v);

=== modified file 'py/_utils.cpp'
--- py/_utils.cpp	2013-05-31 13:27:13 +0000
+++ py/_utils.cpp	2013-09-08 11:12:42 +0000
@@ -7,6 +7,7 @@
 #include<yade/pkg/dem/ScGeom.hpp>
 #include<yade/pkg/dem/DemXDofGeom.hpp>
 #include<yade/pkg/common/Facet.hpp>
+#include<yade/pkg/dem/Tetra.hpp>
 #include<yade/pkg/common/Sphere.hpp>
 #include<yade/pkg/common/NormShearPhys.hpp>
 #include<yade/lib/computational-geometry/Hull2d.hpp>
@@ -543,4 +544,11 @@
 	py::def("growParticles",Shop::growParticles,(py::args("multiplier"), py::args("updateMass")=true, py::args("dynamicOnly")=true), "Change the size of spheres and sphere clumps by the multiplier. If updateMass=True, then the mass is updated. dynamicOnly=True is mandatory in many cases since in current implementation the function would crash on the non-spherical and non-dynamic bodies (e.g. facets, boxes, etc.)");
 	py::def("intrsOfEachBody",intrsOfEachBody,"returns list of lists of interactions of each body");
 	py::def("numIntrsOfEachBody",numIntrsOfEachBody,"returns list of number of interactions of each body");
+	py::def("TetrahedronSignedVolume",static_cast<Real (*)(const vector<Vector3r>&)>(&TetrahedronSignedVolume),"TODO");
+	py::def("TetrahedronVolume",static_cast<Real (*)(const vector<Vector3r>&)>(&TetrahedronVolume),"TODO");
+	py::def("TetrahedronInertiaTensor",TetrahedronInertiaTensor,"TODO");
+	py::def("TetrahedronCentralInertiaTensor",TetrahedronCentralInertiaTensor,"TODO");
+	py::def("TetrahedronWithLocalAxesPrincipal",TetrahedronWithLocalAxesPrincipal,"TODO");
+	py::def("momentum",Shop::momentum,"TODO");
+	py::def("angularMomentum",Shop::angularMomentum,(py::args("origin")=Vector3r(Vector3r::Zero())),"TODO");
 }

=== modified file 'py/utils.py'
--- py/utils.py	2013-08-14 09:03:28 +0000
+++ py/utils.py	2013-09-08 11:12:42 +0000
@@ -348,6 +348,37 @@
 	b.chain=chain
 	return b
 
+def tetra(vertices,strictCheck=True,dynamic=True,fixed=False,wire=True,color=None,highlight=False,noBound=False,material=-1,mask=1,chain=-1):
+	"""Create tetrahedron with given parameters.
+
+	:param [Vector3,Vector3,Vector3,Vector3] vertices: coordinates of vertices in the global coordinate system.
+	:param bool strictCheck: checks vertices order, raise RuntimeError for negative volume
+
+	See :yref:`yade.utils.sphere`'s documentation for meaning of other parameters."""
+	b=Body()
+	center = .25*sum(vertices,Vector3.Zero)
+	volume = TetrahedronSignedVolume(vertices)
+	if volume < 0:
+		if strictCheck:
+			raise RuntimeError, "tetra: wrong order of vertices"
+		temp = vertices[3]
+		vertices[3] = vertices[2]
+		vertices[2] = temp
+		volume = TetrahedronSignedVolume(vertices)
+	assert(volume>0)
+	b.shape = Tetra(v=vertices,color=color if color else randomColor(),wire=wire,highlight=highlight)
+	# modifies pos, ori and inertia
+	ori = TetrahedronWithLocalAxesPrincipal(b)
+	_commonBodySetup(b,volume,b.state.inertia,material,noBound=noBound,pos=center,fixed=fixed)
+	b.state.ori = b.state.refOri = ori
+	b.aspherical = True
+	b.mask = mask
+	b.chain = chain
+	return b
+
+
+
+
 #def setNewVerticesOfFacet(b,vertices):
 #	center = inscribedCircleCenter(vertices[0],vertices[1],vertices[2])
 #	vertices = Vector3(vertices[0])-center,Vector3(vertices[1])-center,Vector3(vertices[2])-center