← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-pkg/yade/git-trunk] Rev 3847: make FlowEngine a template yade class

 

------------------------------------------------------------
revno: 3847
committer: Bruno Chareyre <bruno.chareyre@xxxxxxxxxxxxxxx>
timestamp: Fri 2014-03-21 19:47:45 +0100
message:
  make FlowEngine a template yade class
added:
  pkg/dem/FlowEngine.ipp


--
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 file 'pkg/dem/FlowEngine.ipp'
--- pkg/dem/FlowEngine.ipp	1970-01-01 00:00:00 +0000
+++ pkg/dem/FlowEngine.ipp	2014-03-21 18:47:45 +0000
@@ -0,0 +1,729 @@
+/*************************************************************************
+*  Copyright (C) 2009 by Emanuele Catalano <catalano@xxxxxxxxxxxxxxx>    *
+*  Copyright (C) 2009 by Bruno Chareyre <bruno.chareyre@xxxxxxxxxxx>     *
+*                                                                        *
+*  This program is free software; it is licensed under the terms of the  *
+*  GNU General Public License v2 or later. See file LICENSE for details. *
+*************************************************************************/
+#ifdef YADE_CGAL
+
+#ifdef FLOW_ENGINE
+#include<yade/core/Scene.hpp>
+#include<yade/lib/base/Math.hpp>
+#include<yade/pkg/dem/TesselationWrapper.hpp>
+#include<yade/pkg/common/Sphere.hpp>
+#include<yade/pkg/common/Wall.hpp>
+#include<yade/pkg/common/Box.hpp>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <boost/thread.hpp>
+#include <boost/date_time.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#ifdef LINSOLV
+#include <cholmod.h>
+#endif
+
+#include "FlowEngine.hpp"
+
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::~TemplateFlowEngine() {}
+
+// YADE_PLUGIN((TFlowEng));
+
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+unsigned int TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::imposePressure(Vector3r pos, Real p)
+{
+// 	if (!flow) LOG_ERROR("no flow defined yet, run at least one iter");
+	solver->imposedP.push_back( pair<CGT::Point,Real>(CGT::Point(pos[0],pos[1],pos[2]),p) );
+	//force immediate update of boundary conditions
+	updateTriangulation=true;
+	return solver->imposedP.size()-1;
+}
+
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::action()
+{
+       if ( !isActivated ) return;
+        timingDeltas->start();
+	setPositionsBuffer(true);
+	timingDeltas->checkpoint ( "Position buffer" );
+        if (first) {
+	  if (multithread) setPositionsBuffer(false);
+	  buildTriangulation(pZero,*solver);
+	  initializeVolumes(*solver);
+	  backgroundSolver=solver;
+	  backgroundCompleted=true;
+	}
+	solver->ompThreads = ompThreads>0? ompThreads : omp_get_max_threads();
+
+        timingDeltas->checkpoint ( "Triangulating" );
+	updateVolumes ( *solver );
+        timingDeltas->checkpoint ( "Update_Volumes" );
+	
+        epsVolCumulative += epsVolMax;
+	retriangulationLastIter++;
+	if (!updateTriangulation) updateTriangulation = // If not already set true by another function of by the user, check conditions
+		(defTolerance>0 && epsVolCumulative > defTolerance) || retriangulationLastIter>meshUpdateInterval;
+
+        ///compute flow and and forces here
+	if (pressureForce){
+		solver->gaussSeidel(scene->dt);
+		timingDeltas->checkpoint ( "Gauss-Seidel (includes matrix construct and factorization in single-thread mode)" );
+		solver->computeFacetForcesWithCache();}
+        timingDeltas->checkpoint ( "compute_Forces" );
+        ///Application of vicscous forces
+        scene->forces.sync();
+	timingDeltas->checkpoint ( "forces.sync()" );
+	computeViscousForces ( *solver );
+	timingDeltas->checkpoint ( "viscous forces" );
+	Vector3r force;
+	Vector3r torque;
+        FiniteVerticesIterator verticesEnd = solver->T[solver->currentTes].Triangulation().finite_vertices_end();
+        for ( FiniteVerticesIterator vIt = solver->T[solver->currentTes].Triangulation().finite_vertices_begin(); vIt !=  verticesEnd; vIt++ ) {
+		force = pressureForce ? Vector3r ( vIt->info().forces[0],vIt->info().forces[1],vIt->info().forces[2] ): Vector3r(0,0,0);
+		torque = Vector3r(0,0,0);
+                if (shearLubrication || viscousShear){
+			force = force + solver->shearLubricationForces[vIt->info().id()];
+			torque = torque + solver->shearLubricationTorques[vIt->info().id()];
+			if (pumpTorque)
+				torque = torque + solver->pumpLubricationTorques[vIt->info().id()];
+		}
+		if (twistTorque)
+			torque = torque + solver->twistLubricationTorques[vIt->info().id()];
+		if (normalLubrication)
+			force = force + solver-> normalLubricationForce[vIt->info().id()];
+		scene->forces.addForce ( vIt->info().id(), force);
+		scene->forces.addTorque ( vIt->info().id(), torque);
+        }
+        ///End compute flow and forces
+        timingDeltas->checkpoint ( "Applying Forces" );
+	int sleeping = 0;
+	if (multithread && !first) {
+		while (updateTriangulation && !backgroundCompleted) { /*cout<<"sleeping..."<<sleeping++<<endl;*/
+		  sleeping++;
+		boost::this_thread::sleep(boost::posix_time::microseconds(1000));}
+		if (debug && sleeping) cerr<<"sleeping..."<<sleeping<<endl;
+		if (updateTriangulation || (ellapsedIter>(0.5*meshUpdateInterval) && backgroundCompleted)) {
+			if (debug) cerr<<"switch flow solver"<<endl;
+			if (useSolver==0) LOG_ERROR("background calculations not available for Gauss-Seidel");
+			if (fluidBulkModulus>0) solver->interpolate (solver->T[solver->currentTes], backgroundSolver->T[backgroundSolver->currentTes]);
+			solver=backgroundSolver;
+			backgroundSolver = shared_ptr<FlowSolver> (new FlowSolver);
+			//Copy imposed pressures/flow from the old solver
+			backgroundSolver->imposedP = vector<pair<CGT::Point,Real> >(solver->imposedP);
+			backgroundSolver->imposedF = vector<pair<CGT::Point,Real> >(solver->imposedF);
+			if (debug) cerr<<"switched"<<endl;
+			setPositionsBuffer(false);//set "parallel" buffer for background calculation 
+			backgroundCompleted=false;
+			retriangulationLastIter=ellapsedIter;
+			updateTriangulation=false;
+			epsVolCumulative=0;
+			ellapsedIter=0;
+			boost::thread workerThread(&TemplateFlowEngine::backgroundAction,this);
+			workerThread.detach();
+			if (debug) cerr<<"backgrounded"<<endl;
+			initializeVolumes(*solver);
+			computeViscousForces(*solver);
+			if (debug) cerr<<"volumes initialized"<<endl;
+		}
+		else {
+			if (debug && !backgroundCompleted) cerr<<"still computing solver in the background, ellapsedIter="<<ellapsedIter<<endl;
+			ellapsedIter++;
+		}
+	} else {
+	        if (updateTriangulation && !first) {
+			buildTriangulation (pZero, *solver);
+			initializeVolumes(*solver);
+			computeViscousForces(*solver);
+               		updateTriangulation = false;
+			epsVolCumulative=0;
+			retriangulationLastIter=0;
+			ReTrg++;}
+        }
+        first=false;
+        timingDeltas->checkpoint ( "triangulate + init volumes" );
+}
+
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::backgroundAction()
+{
+	if (useSolver<1) {LOG_ERROR("background calculations not available for Gauss-Seidel"); return;}
+        buildTriangulation ( pZero,*backgroundSolver );
+	//FIXME: GS is computing too much, we need only matrix factorization in fact
+	backgroundSolver->gaussSeidel(scene->dt);
+	//FIXME(2): and here we need only cached variables, not forces
+	backgroundSolver->computeFacetForcesWithCache(/*onlyCache?*/ true);
+// 	boost::this_thread::sleep(boost::posix_time::seconds(5));
+ 	backgroundCompleted = true;
+}
+
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::boundaryConditions ( Solver& flow )
+{
+
+	for (int k=0;k<6;k++)	{
+		flow.boundary (wallIds[k]).flowCondition=!bndCondIsPressure[k];
+                flow.boundary (wallIds[k]).value=bndCondValue[k];
+                flow.boundary (wallIds[k]).velocity = boundaryVelocity[k];//FIXME: needs correct implementation, maybe update the cached pos/vel?
+	}
+}
+
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::setImposedPressure ( unsigned int cond, Real p)
+{
+        if ( cond>=solver->imposedP.size() ) LOG_ERROR ( "Setting p with cond higher than imposedP size." );
+        solver->imposedP[cond].second=p;
+        //force immediate update of boundary conditions
+	solver->pressureChanged=true;
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::imposeFlux ( Vector3r pos, Real flux){
+        solver->imposedF.push_back ( pair<CGT::Point,Real> ( CGT::Point ( pos[0],pos[1],pos[2] ), flux ) );
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::clearImposedPressure () { solver->imposedP.clear(); solver->IPCells.clear();}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::clearImposedFlux () { solver->imposedF.clear(); solver->IFCells.clear();}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+Real TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::getCellFlux ( unsigned int cond)
+{
+	if ( cond>=solver->imposedP.size() ) {LOG_ERROR ( "Getting flux with cond higher than imposedP size." ); return 0;}
+        double flux=0;
+        typename Solver::CellHandle& cell= solver->IPCells[cond];
+        for ( int ngb=0;ngb<4;ngb++ ) {
+                /*if (!cell->neighbor(ngb)->info().Pcondition)*/
+                flux+= cell->info().kNorm() [ngb]* ( cell->info().p()-cell->neighbor ( ngb )->info().p() );
+        }
+        return flux+cell->info().dv();
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::initSolver ( FlowSolver& flow )
+{
+       	flow.Vtotalissimo=0; flow.VSolidTot=0; flow.vPoral=0; flow.sSolidTot=0;
+        flow.slipBoundary=slipBoundary;
+        flow.kFactor = permeabilityFactor;
+        flow.debugOut = debug;
+        flow.useSolver = useSolver;
+	#ifdef EIGENSPARSE_LIB
+	flow.numSolveThreads = numSolveThreads;
+	flow.numFactorizeThreads = numFactorizeThreads;
+	#endif
+	flow.meanKStat = meanKStat;
+        flow.viscosity = viscosity;
+        flow.tolerance=tolerance;
+        flow.relax=relax;
+        flow.clampKValues = clampKValues;
+	flow.maxKdivKmean = maxKdivKmean;
+	flow.minKdivKmean = minKdivKmean;
+        flow.meanKStat = meanKStat;
+        flow.permeabilityMap = permeabilityMap;
+        flow.fluidBulkModulus = fluidBulkModulus;
+        flow.T[flow.currentTes].Clear();
+        flow.T[flow.currentTes].maxId=-1;
+        flow.xMin = 1000.0, flow.xMax = -10000.0, flow.yMin = 1000.0, flow.yMax = -10000.0, flow.zMin = 1000.0, flow.zMax = -10000.0;
+}
+
+#ifdef LINSOLV
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::setForceMetis ( Solver& flow, bool force )
+{
+        if (force) {
+		flow.eSolver.cholmod().nmethods=1;
+		flow.eSolver.cholmod().method[0].ordering=CHOLMOD_METIS;
+	} else cholmod_defaults(&(flow.eSolver.cholmod()));
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+bool TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::getForceMetis ( Solver& flow ) {return (flow.eSolver.cholmod().nmethods==1);}
+#endif
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::buildTriangulation ( Solver& flow )
+{
+        buildTriangulation ( 0.f,flow );
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::buildTriangulation ( double pZero, Solver& flow )
+{
+        flow.resetNetwork();
+	if (first) flow.currentTes=0;
+        else {
+                flow.currentTes=!flow.currentTes;
+                if (debug) cout << "--------RETRIANGULATION-----------" << endl;
+        }
+
+	initSolver(flow);
+
+        addBoundary ( flow );
+        triangulate ( flow );
+        if ( debug ) cout << endl << "Tesselating------" << endl << endl;
+        flow.T[flow.currentTes].compute();
+
+        flow.defineFictiousCells();
+	// For faster loops on cells define this vector
+	flow.T[flow.currentTes].cellHandles.clear();
+	flow.T[flow.currentTes].cellHandles.reserve(flow.T[flow.currentTes].Triangulation().number_of_finite_cells());
+	FiniteCellsIterator cell_end = flow.T[flow.currentTes].Triangulation().finite_cells_end();
+	int k=0;
+	for ( FiniteCellsIterator cell = flow.T[flow.currentTes].Triangulation().finite_cells_begin(); cell != cell_end; cell++ ){
+		flow.T[flow.currentTes].cellHandles.push_back(cell);
+		cell->info().id=k++;}//define unique numbering now, corresponds to position in cellHandles
+        flow.displayStatistics ();
+        flow.computePermeability();
+	//This virtual function does nothing yet, derived class may overload it to make permeability different (see DFN engine)
+	trickPermeability();
+        porosity = flow.vPoralPorosity/flow.vTotalPorosity;
+
+        boundaryConditions ( flow );
+        flow.initializePressure ( pZero );
+	
+        if ( !first && !multithread && (useSolver==0 || fluidBulkModulus>0)) flow.interpolate ( flow.T[!flow.currentTes], flow.T[flow.currentTes] );
+        if ( waveAction ) flow.applySinusoidalPressure ( flow.T[flow.currentTes].Triangulation(), sineMagnitude, sineAverage, 30 );
+        if (normalLubrication || shearLubrication || viscousShear) flow.computeEdgesSurfaces();
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::setPositionsBuffer(bool current)
+{
+	vector<posData>& buffer = current? positionBufferCurrent : positionBufferParallel;
+	buffer.clear();
+	buffer.resize(scene->bodies->size());
+	shared_ptr<Sphere> sph ( new Sphere );
+        const int Sph_Index = sph->getClassIndexStatic();
+	FOREACH ( const shared_ptr<Body>& b, *scene->bodies ) {
+                if (!b || ignoredBody==b->getId()) continue;
+                posData& dat = buffer[b->getId()];
+		dat.id=b->getId();
+		dat.pos=b->state->pos;
+		dat.isSphere= (b->shape->getClassIndex() ==  Sph_Index);
+		if (dat.isSphere) dat.radius = YADE_CAST<Sphere*>(b->shape.get())->radius;
+		dat.exists=true;
+	}
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::addBoundary ( Solver& flow )
+{
+	vector<posData>& buffer = multithread ? positionBufferParallel : positionBufferCurrent;
+        solver->xMin = Mathr::MAX_REAL, solver->xMax = -Mathr::MAX_REAL, solver->yMin = Mathr::MAX_REAL, solver->yMax = -Mathr::MAX_REAL, solver->zMin = Mathr::MAX_REAL, solver->zMax = -Mathr::MAX_REAL;
+        FOREACH ( const posData& b, buffer ) {
+                if ( !b.exists ) continue;
+                if ( b.isSphere ) {
+                        const Real& rad = b.radius;
+                        const Real& x = b.pos[0];
+                        const Real& y = b.pos[1];
+                        const Real& z = b.pos[2];
+                        flow.xMin = min ( flow.xMin, x-rad );
+                        flow.xMax = max ( flow.xMax, x+rad );
+                        flow.yMin = min ( flow.yMin, y-rad );
+                        flow.yMax = max ( flow.yMax, y+rad );
+                        flow.zMin = min ( flow.zMin, z-rad );
+                        flow.zMax = max ( flow.zMax, z+rad );
+                }
+        }
+	//FIXME idOffset must be set correctly, not the case here (always 0), then we need walls first or it will fail
+        idOffset = flow.T[flow.currentTes].maxId+1;
+        flow.idOffset = idOffset;
+        flow.sectionArea = ( flow.xMax - flow.xMin ) * ( flow.zMax-flow.zMin );
+        flow.vTotal = ( flow.xMax-flow.xMin ) * ( flow.yMax-flow.yMin ) * ( flow.zMax-flow.zMin );
+        flow.yMinId=wallIds[ymin];
+        flow.yMaxId=wallIds[ymax];
+        flow.xMaxId=wallIds[xmax];
+        flow.xMinId=wallIds[xmin];
+        flow.zMinId=wallIds[zmin];
+        flow.zMaxId=wallIds[zmax];
+
+        //FIXME: Id's order in boundsIds is done according to the enumeration of boundaries from TXStressController.hpp, line 31. DON'T CHANGE IT!
+        flow.boundsIds[0]= &flow.xMinId;
+        flow.boundsIds[1]= &flow.xMaxId;
+        flow.boundsIds[2]= &flow.yMinId;
+        flow.boundsIds[3]= &flow.yMaxId;
+        flow.boundsIds[4]= &flow.zMinId;
+        flow.boundsIds[5]= &flow.zMaxId;
+
+	for (int k=0;k<6;k++) flow.boundary ( *flow.boundsIds[k] ).useMaxMin = boundaryUseMaxMin[k];
+
+        flow.cornerMin = CGT::Point ( flow.xMin, flow.yMin, flow.zMin );
+        flow.cornerMax = CGT::Point ( flow.xMax, flow.yMax, flow.zMax );
+ 
+        //assign BCs types and values
+        boundaryConditions ( flow );
+
+        double center[3];
+        for ( int i=0; i<6; i++ ) {
+                if ( *flow.boundsIds[i]<0 ) continue;
+                CGT::CVector Normal ( normal[i].x(), normal[i].y(), normal[i].z() );
+                if ( flow.boundary ( *flow.boundsIds[i] ).useMaxMin ) flow.addBoundingPlane(Normal, *flow.boundsIds[i] );
+                else {
+			for ( int h=0;h<3;h++ ) center[h] = buffer[*flow.boundsIds[i]].pos[h];
+// 			cerr << "id="<<*flow.boundsIds[i] <<" center="<<center[0]<<","<<center[1]<<","<<center[2]<<endl;
+                        flow.addBoundingPlane ( center, wallThickness, Normal,*flow.boundsIds[i] );
+                }
+        }
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::triangulate ( Solver& flow )
+{
+///Using Tesselation wrapper (faster)
+// 	TesselationWrapper TW;
+// 	if (TW.Tes) delete TW.Tes;
+// 	TW.Tes = &(flow.T[flow.currentTes]);//point to the current Tes we have in Flowengine
+// 	TW.insertSceneSpheres();//TW is now really inserting in TemplateFlowEngine, using the faster insert(begin,end)
+// 	TW.Tes = NULL;//otherwise, Tes would be deleted by ~TesselationWrapper() at the end of the function.
+///Using one-by-one insertion
+	vector<posData>& buffer = multithread ? positionBufferParallel : positionBufferCurrent;
+	FOREACH ( const posData& b, buffer ) {
+                if ( !b.exists ) continue;
+                if ( b.isSphere ) {
+			if (b.id==ignoredBody) continue;
+                        flow.T[flow.currentTes].insert ( b.pos[0], b.pos[1], b.pos[2], b.radius, b.id );}
+        }
+	flow.T[flow.currentTes].redirected=true;//By inserting one-by-one, we already redirected
+	flow.shearLubricationForces.resize ( flow.T[flow.currentTes].maxId+1 );
+	flow.shearLubricationTorques.resize ( flow.T[flow.currentTes].maxId+1 );
+	flow.pumpLubricationTorques.resize ( flow.T[flow.currentTes].maxId+1 );
+	flow.twistLubricationTorques.resize ( flow.T[flow.currentTes].maxId+1 );
+	flow.shearLubricationBodyStress.resize ( flow.T[flow.currentTes].maxId+1 );
+	flow.normalLubricationForce.resize ( flow.T[flow.currentTes].maxId+1 );
+	flow.normalLubricationBodyStress.resize ( flow.T[flow.currentTes].maxId+1 );
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::initializeVolumes ( Solver& flow )
+{
+	typedef typename Solver::FiniteVerticesIterator FiniteVerticesIterator;
+	
+	FiniteVerticesIterator vertices_end = flow.T[flow.currentTes].Triangulation().finite_vertices_end();
+	CGT::CVector Zero(0,0,0);
+	for (FiniteVerticesIterator V_it = flow.T[flow.currentTes].Triangulation().finite_vertices_begin(); V_it!= vertices_end; V_it++) V_it->info().forces=Zero;
+
+	FOREACH(CellHandle& cell, flow.T[flow.currentTes].cellHandles)
+	{
+		switch ( cell->info().fictious() )
+		{
+			case ( 0 ) : cell->info().volume() = volumeCell ( cell ); break;
+			case ( 1 ) : cell->info().volume() = volumeCellSingleFictious ( cell ); break;
+			case ( 2 ) : cell->info().volume() = volumeCellDoubleFictious ( cell ); break;
+			case ( 3 ) : cell->info().volume() = volumeCellTripleFictious ( cell ); break;
+			default: break; 
+		}
+		if (flow.fluidBulkModulus>0) { cell->info().invVoidVolume() = 1 / ( abs(cell->info().volume()) - flow.volumeSolidPore(cell) ); }
+	}
+	if (debug) cout << "Volumes initialised." << endl;
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::averageRealCellVelocity()
+{
+        solver->averageRelativeCellVelocity();
+        Vector3r Vel ( 0,0,0 );
+        //AVERAGE CELL VELOCITY
+        FiniteCellsIterator cell_end = solver->T[solver->currentTes].Triangulation().finite_cells_end();
+        for ( FiniteCellsIterator cell = solver->T[solver->currentTes].Triangulation().finite_cells_begin(); cell != cell_end; cell++ ) {
+                for ( int g=0;g<4;g++ ) {
+                        if ( !cell->vertex ( g )->info().isFictious ) {
+                                const shared_ptr<Body>& sph = Body::byId ( cell->vertex ( g )->info().id(), scene );
+                                for ( int i=0;i<3;i++ ) Vel[i] = Vel[i] + sph->state->vel[i]/4;
+                        }
+                }
+                RTriangulation& Tri = solver->T[solver->currentTes].Triangulation();
+                CGT::Point pos_av_facet;
+                double volume_facet_translation = 0;
+                CGT::CVector Vel_av ( Vel[0], Vel[1], Vel[2] );
+                for ( int i=0; i<4; i++ ) {
+                        volume_facet_translation = 0;
+                        if ( !Tri.is_infinite ( cell->neighbor ( i ) ) ) {
+                                CGT::CVector Surfk = cell->info()-cell->neighbor ( i )->info();
+                                Real area = sqrt ( Surfk.squared_length() );
+                                Surfk = Surfk/area;
+                                CGT::CVector branch = cell->vertex ( facetVertices[i][0] )->point() - cell->info();
+                                pos_av_facet = ( CGT::Point ) cell->info() + ( branch*Surfk ) *Surfk;
+                                volume_facet_translation += Vel_av*cell->info().facetSurfaces[i];
+                                cell->info().averageVelocity() = cell->info().averageVelocity() - volume_facet_translation/cell->info().volume() * ( pos_av_facet-CGAL::ORIGIN );
+                        }
+                }
+        }
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::updateVolumes ( Solver& flow )
+{
+        if ( debug ) cout << "Updating volumes.............." << endl;
+        Real invDeltaT = 1/scene->dt;
+        epsVolMax=0;
+        Real totVol=0; Real totDVol=0;
+	#ifdef YADE_OPENMP
+	const long size=flow.T[flow.currentTes].cellHandles.size();
+	#pragma omp parallel for num_threads(ompThreads>0 ? ompThreads : 1)
+	for(long i=0; i<size; i++){
+		CellHandle& cell = flow.T[flow.currentTes].cellHandles[i];
+	#else
+	FOREACH(CellHandle& cell, flow.T[flow.currentTes].cellHandles){
+	#endif
+		double newVol, dVol;
+                switch ( cell->info().fictious() ) {
+                	case ( 3 ) : newVol = volumeCellTripleFictious ( cell ); break;
+               		case ( 2 ) : newVol = volumeCellDoubleFictious ( cell ); break;
+                	case ( 1 ) : newVol = volumeCellSingleFictious ( cell ); break;
+			case ( 0 ) : newVol = volumeCell (cell ); break;
+                	default: newVol = 0; break;}
+                dVol=cell->info().volumeSign* ( newVol - cell->info().volume() );
+		cell->info().dv() = dVol*invDeltaT;
+                cell->info().volume() = newVol;
+		if (defTolerance>0) { //if the criterion is not used, then we skip these updates a save a LOT of time when Nthreads > 1
+			#pragma omp atomic
+			totVol+=newVol;
+			#pragma omp atomic
+                	totDVol+=abs(dVol);}
+        }
+	if (defTolerance>0)  epsVolMax = totDVol/totVol;
+	for (unsigned int n=0; n<flow.imposedF.size();n++) {
+		flow.IFCells[n]->info().dv()+=flow.imposedF[n].second;
+		flow.IFCells[n]->info().Pcondition=false;}
+        if ( debug ) cout << "Updated volumes, total =" <<totVol<<", dVol="<<totDVol<<endl;
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+template<class Cellhandle>
+Real TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::volumeCellSingleFictious ( Cellhandle cell )
+{
+        Vector3r V[3];
+        int b=0;
+        int w=0;
+        cell->info().volumeSign=1;
+        Real Wall_coordinate=0;
+
+        for ( int y=0;y<4;y++ ) {
+                if ( ! ( cell->vertex ( y )->info().isFictious ) ) {
+                        V[w]=positionBufferCurrent[cell->vertex ( y )->info().id()].pos;
+			w++;
+                } else {
+                        b = cell->vertex ( y )->info().id();
+                        const shared_ptr<Body>& wll = Body::byId ( b , scene );
+                        if ( !solver->boundary ( b ).useMaxMin ) Wall_coordinate = wll->state->pos[solver->boundary ( b ).coordinate]+ ( solver->boundary ( b ).normal[solver->boundary ( b ).coordinate] ) *wallThickness/2.;
+                        else Wall_coordinate = solver->boundary ( b ).p[solver->boundary ( b ).coordinate];
+                }
+        }
+        Real Volume = 0.5* ( ( V[0]-V[1] ).cross ( V[0]-V[2] ) ) [solver->boundary ( b ).coordinate] * ( 0.33333333333* ( V[0][solver->boundary ( b ).coordinate]+ V[1][solver->boundary ( b ).coordinate]+ V[2][solver->boundary ( b ).coordinate] ) - Wall_coordinate );
+        return abs ( Volume );
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+template<class Cellhandle>
+Real TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::volumeCellDoubleFictious ( Cellhandle cell )
+{
+        Vector3r A=Vector3r::Zero(), AS=Vector3r::Zero(),B=Vector3r::Zero(), BS=Vector3r::Zero();
+
+        cell->info().volumeSign=1;
+        int b[2];
+        int coord[2];
+        Real Wall_coordinate[2];
+        int j=0;
+        bool first_sph=true;
+
+        for ( int g=0;g<4;g++ ) {
+                if ( cell->vertex ( g )->info().isFictious ) {
+                        b[j] = cell->vertex ( g )->info().id();
+                        coord[j]=solver->boundary ( b[j] ).coordinate;
+                        if ( !solver->boundary ( b[j] ).useMaxMin ) Wall_coordinate[j] = positionBufferCurrent[b[j]].pos[coord[j]] + ( solver->boundary ( b[j] ).normal[coord[j]] ) *wallThickness/2.;
+                        else Wall_coordinate[j] = solver->boundary ( b[j] ).p[coord[j]];
+                        j++;
+                } else if ( first_sph ) {
+                        A=AS=/*AT=*/ positionBufferCurrent[cell->vertex(g)->info().id()].pos;
+                        first_sph=false;
+                } else {
+                        B=BS=/*BT=*/ positionBufferCurrent[cell->vertex(g)->info().id()].pos;;
+                }
+        }
+        AS[coord[0]]=BS[coord[0]] = Wall_coordinate[0];
+
+        //first pyramid with triangular base (A,B,BS)
+        Real Vol1=0.5* ( ( A-BS ).cross ( B-BS ) ) [coord[1]]* ( 0.333333333* ( 2*B[coord[1]]+A[coord[1]] )-Wall_coordinate[1] );
+        //second pyramid with triangular base (A,AS,BS)
+        Real Vol2=0.5* ( ( AS-BS ).cross ( A-BS ) ) [coord[1]]* ( 0.333333333* ( B[coord[1]]+2*A[coord[1]] )-Wall_coordinate[1] );
+        return abs ( Vol1+Vol2 );
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+template<class Cellhandle>
+Real TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::volumeCellTripleFictious ( Cellhandle cell )
+{
+        Vector3r A;
+
+        int b[3];
+        int coord[3];
+        Real Wall_coordinate[3];
+        int j=0;
+        cell->info().volumeSign=1;
+
+        for ( int g=0;g<4;g++ ) {
+                if ( cell->vertex ( g )->info().isFictious ) {
+                        b[j] = cell->vertex ( g )->info().id();
+                        coord[j]=solver->boundary ( b[j] ).coordinate;
+                        const shared_ptr<Body>& wll = Body::byId ( b[j] , scene );
+                        if ( !solver->boundary ( b[j] ).useMaxMin ) Wall_coordinate[j] = wll->state->pos[coord[j]] + ( solver->boundary ( b[j] ).normal[coord[j]] ) *wallThickness/2.;
+                        else Wall_coordinate[j] = solver->boundary ( b[j] ).p[coord[j]];
+                        j++;
+                } else {
+                        const shared_ptr<Body>& sph = Body::byId ( cell->vertex ( g )->info().id(), scene );
+                        A= ( sph->state->pos );
+                }
+        }
+        Real Volume = ( A[coord[0]]-Wall_coordinate[0] ) * ( A[coord[1]]-Wall_coordinate[1] ) * ( A[coord[2]]-Wall_coordinate[2] );
+        return abs ( Volume );
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+template<class Cellhandle>
+Real TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::volumeCell ( Cellhandle cell )
+{
+	static const Real inv6 = 1/6.;
+	const Vector3r& p0 = positionBufferCurrent[cell->vertex ( 0 )->info().id()].pos;
+	const Vector3r& p1 = positionBufferCurrent[cell->vertex ( 1 )->info().id()].pos;
+	const Vector3r& p2 = positionBufferCurrent[cell->vertex ( 2 )->info().id()].pos;
+	const Vector3r& p3 = positionBufferCurrent[cell->vertex ( 3 )->info().id()].pos;
+	Real volume = inv6 * ((p0-p1).cross(p0-p2)).dot(p0-p3);
+        if ( ! ( cell->info().volumeSign ) ) cell->info().volumeSign= ( volume>0 ) ?1:-1;
+        return volume;
+}
+template< class _CellInfo, class _VertexInfo, class _Tesselation, class solverT >
+void TemplateFlowEngine<_CellInfo,_VertexInfo,_Tesselation,solverT>::computeViscousForces ( Solver& flow )
+{
+	if (normalLubrication || shearLubrication || viscousShear){
+		if ( debug ) cout << "Application of viscous forces" << endl;
+		if ( debug ) cout << "Number of edges = " << flow.edgeIds.size() << endl;
+		for ( unsigned int k=0; k<flow.shearLubricationForces.size(); k++ ) flow.shearLubricationForces[k]=Vector3r::Zero();
+		for ( unsigned int k=0; k<flow.shearLubricationTorques.size(); k++ ) flow.shearLubricationTorques[k]=Vector3r::Zero();
+		for ( unsigned int k=0; k<flow.pumpLubricationTorques.size(); k++ ) flow.pumpLubricationTorques[k]=Vector3r::Zero();
+		for ( unsigned int k=0; k<flow.twistLubricationTorques.size(); k++ ) flow.twistLubricationTorques[k]=Vector3r::Zero();
+		for ( unsigned int k=0; k<flow.shearLubricationBodyStress.size(); k++) flow.shearLubricationBodyStress[k]=Matrix3r::Zero();
+		for ( unsigned int k=0; k<flow.normalLubricationForce.size(); k++ ) flow.normalLubricationForce[k]=Vector3r::Zero();
+		for ( unsigned int k=0; k<flow.normalLubricationBodyStress.size(); k++) flow.normalLubricationBodyStress[k]=Matrix3r::Zero();
+
+		typedef typename Solver::Tesselation Tesselation;
+		const Tesselation& Tes = flow.T[flow.currentTes];
+		flow.deltaShearVel.clear(); flow.normalV.clear(); flow.deltaNormVel.clear(); flow.surfaceDistance.clear(); flow.onlySpheresInteractions.clear(); flow.normalStressInteraction.clear(); flow.shearStressInteraction.clear();
+
+
+		for ( int i=0; i< ( int ) flow.edgeIds.size(); i++ ) {
+			const int& id1 = flow.edgeIds[i].first;
+			const int& id2 = flow.edgeIds[i].second;
+			
+			int hasFictious= Tes.vertex ( id1 )->info().isFictious +  Tes.vertex ( id2 )->info().isFictious;
+			if (hasFictious>0 or id1==id2) continue;
+			const shared_ptr<Body>& sph1 = Body::byId ( id1, scene );
+			const shared_ptr<Body>& sph2 = Body::byId ( id2, scene );
+			Sphere* s1=YADE_CAST<Sphere*> ( sph1->shape.get() );
+			Sphere* s2=YADE_CAST<Sphere*> ( sph2->shape.get() );
+			const Real& r1 = s1->radius;
+			const Real& r2 = s2->radius;
+			Vector3r deltaV; Real deltaNormV; Vector3r deltaShearV;
+			Vector3r O1O2Vector; Real O1O2; Vector3r normal; Real surfaceDist; Vector3r O1CVector; Vector3r O2CVector;Real meanRad ;Real Rh; Vector3r deltaAngVel; Vector3r deltaShearAngVel;
+			Vector3r shearLubF; Vector3r normaLubF; Vector3r pumpT; Vector3r deltaAngNormVel; Vector3r twistT; Vector3r angVel1; Vector3r angVel2; 
+		//FIXME: if periodic and velGrad!=0, then deltaV should account for velGrad, not the case currently
+			if ( !hasFictious ){
+				O1O2Vector = sph2->state->pos + makeVector3r(Tes.vertex(id2)->info().ghostShift()) - sph1->state->pos - makeVector3r(Tes.vertex(id1)->info().ghostShift());
+				O1O2 = O1O2Vector.norm(); 
+				normal= (O1O2Vector/O1O2);
+				surfaceDist = O1O2 - r2 - r1;
+				O1CVector = (O1O2/2. + (pow(r1,2) - pow(r2,2)) / (2.*O1O2))*normal;
+				O2CVector = -(O1O2Vector - O1CVector);
+				meanRad = (r2 + r1)/2.;
+				Rh = (r1 < r2)? surfaceDist + 0.45 * r1 : surfaceDist + 0.45 * r2;
+				deltaV = (sph2->state->vel + sph2->state->angVel.cross(-r2 * normal)) - (sph1->state->vel+ sph1->state->angVel.cross(r1 * normal));
+				angVel1 = sph1->state->angVel;
+				angVel2 = sph2->state->angVel;
+				deltaAngVel = sph2->state->angVel - sph1->state->angVel;
+
+			} else {
+				if ( hasFictious==1 ) {//for the fictious sphere, use velocity of the boundary, not of the body
+					bool v1fictious = Tes.vertex ( id1 )->info().isFictious;
+					int bnd = v1fictious? id1 : id2;
+					int coord = flow.boundary(bnd).coordinate;
+					O1O2 = v1fictious ? abs((sph2->state->pos + makeVector3r(Tes.vertex(id2)->info().ghostShift()))[coord] - flow.boundary(bnd).p[coord]) : abs((sph1->state->pos + makeVector3r(Tes.vertex(id1)->info().ghostShift()))[coord] - flow.boundary(bnd).p[coord]);
+					if(v1fictious)
+						normal = makeVector3r(flow.boundary(id1).normal);
+					else
+						normal = -makeVector3r(flow.boundary(id2).normal);
+					O1O2Vector = O1O2 * normal;
+					meanRad = v1fictious ? r2:r1;
+					surfaceDist = O1O2 - meanRad;
+					if (v1fictious){
+						O1CVector = Vector3r::Zero();
+						O2CVector = - O1O2Vector;}
+					else{
+						O1CVector =  O1O2Vector;
+						O2CVector = Vector3r::Zero();}
+				
+					Rh = surfaceDist + 0.45 * meanRad;
+					Vector3r v1 = ( Tes.vertex ( id1 )->info().isFictious ) ? flow.boundary ( id1 ).velocity:sph1->state->vel + sph1->state->angVel.cross(r1 * normal);
+					Vector3r v2 = ( Tes.vertex ( id2 )->info().isFictious ) ? flow.boundary ( id2 ).velocity:sph2->state->vel + sph2->state->angVel.cross(-r2 * (normal));
+					deltaV = v2-v1;
+					angVel1 = ( Tes.vertex ( id1 )->info().isFictious ) ? Vector3r::Zero() : sph1->state->angVel;
+					angVel2 = ( Tes.vertex ( id2 )->info().isFictious ) ? Vector3r::Zero() : sph2->state->angVel;
+					deltaAngVel = angVel2 - angVel1;
+				}
+			}
+			deltaShearV = deltaV - ( normal.dot ( deltaV ) ) *normal;
+			deltaShearAngVel = deltaAngVel - ( normal.dot ( deltaAngVel ) ) *normal;
+			flow.deltaShearVel.push_back(deltaShearV);
+			flow.normalV.push_back(normal);
+			flow.surfaceDistance.push_back(max(surfaceDist, 0.) + eps*meanRad);
+
+			/// Compute the  shear Lubrication force and torque on each particle
+			
+			if (shearLubrication)
+				shearLubF = flow.computeShearLubricationForce(deltaShearV,surfaceDist,i,eps,O1O2,meanRad);
+			else if (viscousShear) 
+				shearLubF = flow.computeViscousShearForce ( deltaShearV, i , Rh);
+				
+			if (viscousShear || shearLubrication){
+
+				flow.shearLubricationForces[id1]+=shearLubF;
+				flow.shearLubricationForces[id2]+=(-shearLubF);
+				flow.shearLubricationTorques[id1]+=O1CVector.cross(shearLubF);
+				flow.shearLubricationTorques[id2]+=O2CVector.cross(-shearLubF);
+				
+				/// Compute the  pump Lubrication torque on each particle
+				
+				if (pumpTorque){
+					pumpT = flow.computePumpTorque(deltaShearAngVel, surfaceDist, i, eps, meanRad );
+					flow.pumpLubricationTorques[id1]+=(-pumpT);
+					flow.pumpLubricationTorques[id2]+=pumpT;}
+				
+				/// Compute the  twist Lubrication torque on each particle
+				
+				if (twistTorque){
+					deltaAngNormVel = (normal.dot(deltaAngVel))*normal ;
+					twistT = flow.computeTwistTorque(deltaAngNormVel, surfaceDist, i, eps, meanRad );
+					flow.twistLubricationTorques[id1]+=(-twistT);
+					flow.twistLubricationTorques[id2]+=twistT;
+				}
+			}		
+			/// Compute the viscous shear stress on each particle
+			
+			if (viscousShearBodyStress){
+				flow.shearLubricationBodyStress[id1] += shearLubF * O1CVector.transpose()/ (4.0/3.0 *3.14* pow(r1,3));
+				flow.shearLubricationBodyStress[id2] += (-shearLubF) * O2CVector.transpose()/ (4.0/3.0 *3.14* pow(r2,3));
+				flow.shearStressInteraction.push_back(shearLubF * O1O2Vector.transpose()/(4.0/3.0 *3.14* pow(r1,3)));
+				}
+
+			/// Compute the normal lubrication force applied on each particle
+			
+			if (normalLubrication){
+				deltaNormV = normal.dot(deltaV);
+				flow.deltaNormVel.push_back(deltaNormV * normal);
+				normaLubF = flow.computeNormalLubricationForce (deltaNormV, surfaceDist, i,eps,stiffness,scene->dt,meanRad)*normal;
+				flow.normalLubricationForce[id1]+=normaLubF;
+				flow.normalLubricationForce[id2]+=(-normaLubF);
+
+				/// Compute the normal lubrication stress on each particle
+				
+				if (viscousNormalBodyStress){
+					flow.normalLubricationBodyStress[id1] += normaLubF * O1CVector.transpose()/ (4.0/3.0 *3.14* pow(r1,3));
+					flow.normalLubricationBodyStress[id2] += (-normaLubF) *O2CVector.transpose() / (4.0/3.0 *3.14* pow(r2,3));
+					flow.normalStressInteraction.push_back(normaLubF * O1O2Vector.transpose()/(4.0/3.0 *3.14* pow(r1,3)));
+				}
+			}
+			
+			if (!hasFictious)
+				flow.onlySpheresInteractions.push_back(i);
+				
+		}
+	}
+}
+
+#endif //FLOW_ENGINE
+
+#endif /* YADE_CGAL */
+