← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 2715: - Calculation of fabric tensor takes care of the sign of normal force (I think this is the way to...

 

------------------------------------------------------------
revno: 2715
committer: Chiara Modenese <c.modenese@xxxxxxxxx>
branch nick: yade
timestamp: Tue 2011-02-08 10:17:07 +0000
message:
  - Calculation of fabric tensor takes care of the sign of normal force (I think this is the way to do it if we have cohesive forces but I will check again in the existing literature). Also the tensor can be split into two parts according to the mean contact force or a threshold value if this is going to be specified (thanks Vincent for suggestion).
modified:
  pkg/dem/Shop.cpp
  pkg/dem/Shop.hpp
  py/_utils.cpp


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

Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-dev/yade/trunk/+edit-subscription
=== modified file 'pkg/dem/Shop.cpp'
--- pkg/dem/Shop.cpp	2011-02-07 09:00:07 +0000
+++ pkg/dem/Shop.cpp	2011-02-08 10:17:07 +0000
@@ -533,11 +533,13 @@
 
 /* Return the fabric tensor as according to [Satake1982]. */
 /* as side-effect, set Gl1_NormShear::strongWeakThresholdForce */
-py::tuple Shop::fabricTensor(bool splitTensor){
+py::tuple Shop::fabricTensor(bool splitTensor, bool compressionPositive, Real thresholdForce){
 	Scene* scene=Omega::instance().getScene().get();
 	if (!scene->isPeriodic){ throw runtime_error("Can't compute fabric tensor of periodic cell in aperiodic simulation."); }
+	
+	// *** Fabric tensor ***/
 	Matrix3r fabric(Matrix3r::Zero()); 
-	int count=0; 
+	int count=0; // number of interactions
 	FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
 		if(!I->isReal()) continue;
 		GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
@@ -551,14 +553,16 @@
 	fabric(1,0)=fabric(0,1); fabric(2,0)=fabric(0,2); fabric(2,1)=fabric(1,2);
 	fabric/=count;
 	
+	// *** Average contact force ***/
 	// calculate average contact force
 	Real Fmean(0);
 	FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
 		if(!I->isReal()) continue;
 		GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
 		NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
-		Vector3r Fcontact=phys->shearForce+phys->normalForce;
-		Real f=Fcontact.norm();
+		const Vector3r& n=geom->normal;
+		Real f=(compressionPositive?-1:1)*phys->normalForce.dot(n);  // FIXME: is it right to consider the sign here?
+		//Real f=phys->normalForce.norm();
 		Fmean+=f;
 	}
 	Fmean/=count; 
@@ -567,18 +571,24 @@
 		Gl1_NormPhys::maxWeakFn=Fmean;
 	#endif
 	
+	// *** Weak and strong fabric tensors ***/
 	// evaluate two different parts of the fabric tensor 
 	// make distinction between strong and weak network of contact forces
 	Matrix3r fabricStrong(Matrix3r::Zero()), fabricWeak(Matrix3r::Zero()); 
-	int nStrong(0), nWeak(0);
+	int nStrong(0), nWeak(0); // number of strong and weak contacts respectively
+	if (!splitTensor & !isnan(thresholdForce)) {LOG_WARN("The bool splitTensor should be set to True if you specified a threshold value for the contact force, otherwise the function will return only the fabric tensor and not the two separate contributions.");}
 	FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
 		if(!I->isReal()) continue;
 		GenericSpheresContact* geom=YADE_CAST<GenericSpheresContact*>(I->geom.get());
 		NormShearPhys* phys=YADE_CAST<NormShearPhys*>(I->phys.get());
-		Vector3r Fcontact=phys->shearForce+phys->normalForce;
-		Real f=Fcontact.norm();
 		const Vector3r& n=geom->normal;
-		if (f>Fmean){
+		Real  f=(compressionPositive?-1:1)*phys->normalForce.dot(n); 
+		//Real f=phys->normalForce.norm();
+		// FIXME: is it right to check only the normal force against Fmean?
+		// what if we have adhesive contacts?
+		// slipt the tensor according to the mean contact force or a threshold value if this is given
+		Real Fsplit=(!isnan(thresholdForce))?thresholdForce:Fmean;
+		if (f>Fsplit){ // forces are compared with their sign
 			for(int i=0; i<3; i++) for(int j=i; j<3; j++){
 				fabricStrong(i,j)+=n[i]*n[j];
 			}
@@ -597,7 +607,16 @@
 	fabricStrong/=nStrong;
 	fabricWeak/=nWeak;
 	
-	if (!splitTensor){return py::make_tuple(fabric);}
+	// *** Compute total fabric tensor from the two tensors above ***/
+	Matrix3r fabricTot(Matrix3r::Zero()); 
+	int q(0);
+	if(!count==0){ // compute only if there are some interactions
+		q=nStrong*1./count; 
+		fabricTot=(1-q)*fabricWeak+q*fabricStrong;
+	}
+	
+	// return fabric tensor or alternatively the two distinct contributions according to strong and weak subnetworks
+	if (!splitTensor){return py::make_tuple(fabric);} // return fabricTot for the 
 	else{return py::make_tuple(fabricStrong,fabricWeak);}
 }
 

=== modified file 'pkg/dem/Shop.hpp'
--- pkg/dem/Shop.hpp	2011-02-04 18:00:01 +0000
+++ pkg/dem/Shop.hpp	2011-02-08 10:17:07 +0000
@@ -115,5 +115,5 @@
 		static py::tuple normalShearStressTensors(bool compressionPositive=false);
 		
 		//! Function to compute fabric tensor of periodic cell
-		static py::tuple fabricTensor(bool splitTensor=false);
+		static py::tuple fabricTensor(bool splitTensor=false, bool compressionPositive=true, Real thresholdForce=NaN);
 };

=== modified file 'py/_utils.cpp'
--- py/_utils.cpp	2011-02-04 18:00:01 +0000
+++ py/_utils.cpp	2011-02-08 10:17:07 +0000
@@ -429,7 +429,7 @@
 Real Shop__getPorosity(Real volume=-1){ return Shop::getPorosity(Omega::instance().getScene(),volume); }
 
 Matrix3r Shop__stressTensorOfPeriodicCell(bool smallStrains=false){return Shop::stressTensorOfPeriodicCell(smallStrains);}
-py::tuple Shop__fabricTensor(bool splitTensor=false){return Shop::fabricTensor(splitTensor);}
+py::tuple Shop__fabricTensor(bool splitTensor=false, bool compressionPositive=true, Real thresholdForce=NaN){return Shop::fabricTensor(splitTensor,compressionPositive,thresholdForce);}
 py::tuple Shop__normalShearStressTensors(bool compressionPositive=false){return Shop::normalShearStressTensors(compressionPositive);}
 py::tuple Shop__getStressLWForEachBody(bool revertSign=false){return Shop::getStressLWForEachBody(revertSign);}
 
@@ -476,7 +476,7 @@
 	py::def("getViscoelasticFromSpheresInteraction",getViscoelasticFromSpheresInteraction,(py::arg("tc"),py::arg("en"),py::arg("es")),"Compute viscoelastic interaction parameters from analytical solution of a pair spheres collision problem:\n\n\n.. math::\n\t:nowrap:\n\n\n\t\\begin{align*}k_n&=\\frac{m}{t_c^2}\\left(\\pi^2+(\\ln e_n)^2\\right)\\\\  c_n&=-\\frac{2m}{t_c}\\ln e_n \\\\k_t&=\\frac27\\frac{m}{t_c^2}\\left(\\pi^2+(\\ln e_t)^2\\right)  \\\\ c_t=-\\frac27\\frac{m}{t_c}\\ln e_t \\end{align*}\n\n\nwhere $k_n$, $c_n$ are normal elastic and viscous coefficients and $k_t$, $c_t$ shear elastic and viscous coefficients. For details see [Pournin2001]_.\n\n:param float m: sphere mass $m$\n:param float tc: collision time $t_c$\n:param float en: normal restitution coefficient $e_n$\n:param float es: tangential restitution coefficient $e_s$\n:return: dictionary with keys ``kn`` (the value of $k_n$), ``cn`` ($c_n$), ``kt`` ($k_t$), ``ct`` ($c_t$).");
 	py::def("stressTensorOfPeriodicCell",Shop__stressTensorOfPeriodicCell,(py::args("smallStrains")=false),"Compute overall (macroscopic) stress of periodic cell using equation published in [Kuhl2001]_:\n\n.. math:: \\vec{\\sigma}=\\frac{1}{V}\\sum_cl^c[\\vec{N}^cf_N^c+\\vec{T}^{cT}\\cdot\\vec{f}^c_T],\n\nwhere $V$ is volume of the cell, $l^c$ length of interaction $c$, $f^c_N$ normal force and $\\vec{f}^c_T$ shear force. Sumed are values over all interactions $c$. $\\vec{N}^c$ and $\\vec{T}^{cT}$ are projection tensors (see the original publication for more details):\n\n.. math:: \\vec{N}=\\vec{n}\\otimes\\vec{n}\\rightarrow N_{ij}=n_in_j\n\n.. math:: \\vec{T}^T=\\vec{I}_{sym}\\cdot\\vec{n}-\\vec{n}\\otimes\\vec{n}\\otimes\\vec{n}\\rightarrow T^T_{ijk}=\\frac{1}{2}(\\delta_{ik}\\delta_{jl}+\\delta_{il}\\delta_{jk})n_l-n_in_jn_k\n\n.. math:: \\vec{T}^T\\cdot\\vec{f}_T\\equiv T^T_{ijk}f_k=(\\delta_{ik}n_j/2+\\delta_{jk}n_i/2-n_in_jn_k)f_k=n_jf_i/2+n_if_j/2-n_in_jn_kf_k,\n\nwhere $n$ is unit vector oriented along the interaction (:yref:`normal<GenericSpheresContact::normal>`) and $\\delta$ is Kronecker's delta. As $\\vec{n}$ and $\\vec{f}_T$ are perpendicular (therfore $n_if_i=0$) we can write\n\n.. math:: \\sigma_{ij}=\\frac{1}{V}\\sum l[n_in_jf_N+n_jf^T_i/2+n_if^T_j/2]\n\n:param bool smallStrains: if false (large strains), real values of volume and interaction lengths are computed. If true, only :yref:`refLength<Dem3DofGeom::refLength>` of interactions and initial volume are computed (can save some time).\n\n:return: macroscopic stress tensor as Matrix3");
 	py::def("normalShearStressTensors",Shop__normalShearStressTensors,(py::args("compressionPositive")=false),"Compute overall stress tensor of the periodic cell decomposed in 2 parts, one contributed by normal forces, the other by shear forces. The formulation can be found in [Thornton2000]_, eq. (3):\n\n.. math:: \\tens{\\sigma}_{ij}=\\frac{2}{V}\\sum R N \\vec{n}_i \\vec{n}_j+\\frac{2}{V}\\sum R T \\vec{n}_i\\vec{t}_j\n\nwhere $V$ is the cell volume, $R$ is \"contact radius\" (in our implementation, current distance between particle centroids), $\\vec{n}$ is the normal vector, $\\vec{t}$ is a vector perpendicular to $\\vec{n}$, $N$ and $T$ are norms of normal and shear forces.");
-	py::def("fabricTensor",Shop__fabricTensor,(py::args("splitTensor")=false),"Compute the fabric tensor of the periodic cell. The original paper can be found in [Satake1982]_.\n\n:param bool splitTensor: split the fabric tensor into two parts related to the strong and weak contact forces respectively.");
+	py::def("fabricTensor",Shop__fabricTensor,(py::args("splitTensor")=false,py::args("compressionPositive")=true,py::args("thresholdForce")=NaN),"Compute the fabric tensor of the periodic cell. The original paper can be found in [Satake1982]_.\n\n:param bool splitTensor: split the fabric tensor into two parts related to the strong and weak contact forces respectively.\n\n:param bool compressionPositive: compressive forces are considered as positive.\n\n:param Real thresholdForce: if the fabric tensor is split into two parts, a threshold value can be specified otherwise the mean contact force is considered by default.");
 	py::def("bodyStressTensors",Shop__getStressLWForEachBody,(py::args("revertSign")=false),"Compute and return a table with per-particle stress tensors. Each tensor represents the average stress in one particle, obtained from the contour integral of applied load as detailed below. This definition is considering each sphere as a continuum. It can be considered exact in the context of spheres at static equilibrium, interacting at contact points with negligible volume changes of the solid phase (this last assumption is not restricting possible deformations and volume changes at the packing scale).\n\nProof:\n\nFirst, we remark the identity:  $\\sigma_{ij}=\\delta_{ij}\\sigma_{ij}=x_{i,j}\\sigma_{ij}=(x_{i}\\sigma_{ij})_{,j}-x_{i}\\sigma_{ij,j}$.\n\nAt equilibrium, the divergence of stress is null: $\\sigma_{ij,j}=\\vec{0}$. Consequently, after divergence theorem: $\\frac{1}{V}\\int_V \\sigma_{ij}dV = \\frac{1}{V}\\int_V (x_{i}\\sigma_{ij})_{,j}dV = \\frac{1}{V}\\int_{\\partial V}x_i.\\sigma_{ij}.\\vec{n_j}.dS = \\frac{1}{V}\\sum_kx_i^k.f_j^k$.\n\nThe last equality is implicitely based on the representation of external loads as Dirac distributions whose zeros are the so-called *contact points*: 0-sized surfaces on which the *contact forces* are applied, located at $x_i$ in the deformed configuration.\n\nA weighted average of per-body stresses will give the average stress inside the solid phase. There is a simple relation between the stress inside the solid phase and the stress in an equivalent continuum in the absence of fluid pressure. For porosity $n$, the relation reads: $\\sigma_{ij}^{equ.}=(1-n)\\sigma_{ij}^{solid}$.\n\n:param bool revertSign: invert the sign of returned tensors components.");
 	py::def("maxOverlapRatio",maxOverlapRatio,"Return maximum overlap ration in interactions (with :yref:`ScGeom`) of two :yref:`spheres<Sphere>`. The ratio is computed as $\\frac{u_N}{2(r_1 r_2)/r_1+r_2}$, where $u_N$ is the current overlap distance and $r_1$, $r_2$ are radii of the two spheres in contact.");
 }