void GeomData::transferCijData(pMesh theMesh){
		dblarray Cij(3,.0);
		double cij[3], norm;
		int ndom = getNumDomains();
		for (int i=0; i<ndom; i++){
			int dom = domainList[i];
			int row = 0;
			EIter eit = M_edgeIter(theMesh);
			while ( pEdge edge = EIter_next(eit) ){
				if ( edgeBelongToDomain(edge,dom) ){
					getCij(edge,dom,Cij);
					norm = getCij_norm(edge,dom);
					cij[0] = Cij[0];
					cij[1] = Cij[1];
					cij[2] = Cij[2];
					setCij(i,row,cij);
					setCij_norm(i,row,norm);
					row++;
				}
			}
			EIter_delete(eit);
		}
	}
Esempio n. 2
0
void mexFunction(
	int nlhs, mxArray *plhs[],
	int nrhs, const mxArray *prhs[])
{
	double *C ;
	double *XYZva, *ind_tra, *XYZvb, *ind_trb, weight, defl ;
	unsigned int mXYZva, nXYZva, minda, ninda ;
	unsigned int mXYZvb, nXYZvb, mindb, nindb ;

	if (nrhs!=6) {
		mexErrMsgTxt("6 Inputs : XYZva, ind_tra, XYZvb, ind_trb, weight, defl") ;
	} else if (nlhs>1) {
		mexErrMsgTxt("1 Outputs : Cij_cog") ;
	}

	mXYZva=mxGetM(prhs[0]) ;
	nXYZva=mxGetN(prhs[0]) ;
	if (!mxIsNumeric(prhs[0]) || mxIsComplex(prhs[0]) 
		|| mxIsSparse(prhs[0]) || (nXYZva!=3)) {
		mexErrMsgTxt("XYZva must be a matrix : Nverta x 3") ;
	}

	minda=mxGetM(prhs[1]) ;
	ninda=mxGetN(prhs[1]) ;
	if (!mxIsNumeric(prhs[1]) || mxIsComplex(prhs[1]) 
		|| mxIsSparse(prhs[1]) || !(ninda==3)) {
		mexErrMsgTxt("ind_tra must be a matrix : Ntria x 3") ;
	}

	mXYZvb=mxGetM(prhs[2]) ;
	nXYZvb=mxGetN(prhs[2]) ;
	if (!mxIsNumeric(prhs[2]) || mxIsComplex(prhs[2]) 
		|| mxIsSparse(prhs[2]) || (nXYZvb!=3)) {
		mexErrMsgTxt("XYZvb must be a matrix : Nvertb") ;
	}

	mindb=mxGetM(prhs[3]) ;
	nindb=mxGetN(prhs[3]) ;
	if (!mxIsNumeric(prhs[3]) || mxIsComplex(prhs[3]) 
		|| mxIsSparse(prhs[3]) || !(nindb==3)) {
		mexErrMsgTxt("ind_trb must be a matrix : Ntrib x 3") ;
	}
	
	if (!mxIsNumeric(prhs[4]) || mxIsComplex(prhs[4])
		|| mxIsSparse(prhs[4]) || !mxIsDouble(prhs[4])
		|| mxGetN(prhs[4])*mxGetM(prhs[4])!=1 ) {
		mexErrMsgTxt("weight must be a scalar") ;
	}

	if (!mxIsNumeric(prhs[5]) || mxIsComplex(prhs[5])
		|| mxIsSparse(prhs[5]) || !mxIsDouble(prhs[5])
		|| mxGetN(prhs[5])*mxGetM(prhs[5])!=1 ) {
		mexErrMsgTxt("defl must be a scalar") ;
	}

	plhs[0]=mxCreateDoubleMatrix(minda,mindb,mxREAL) ;

	C=mxGetPr(plhs[0]) ;
	XYZva=mxGetPr(prhs[0]) ;
	ind_tra=mxGetPr(prhs[1]) ;
	XYZvb=mxGetPr(prhs[2]) ;
	ind_trb=mxGetPr(prhs[3]) ;
	weight = mxGetScalar(prhs[4]) ;
	defl = mxGetScalar(prhs[5]) ;

	Cij(C,XYZva,mXYZva,ind_tra,minda, 
		XYZvb,mXYZvb,ind_trb,mindb,weight,defl);

	mxSetPr(plhs[0],C) ;
}
Esempio n. 3
0
// calculates: Cij, Dij and nodal volume
int EBFV1_preprocessor_2D(pMesh theMesh, void *pData, int &ndom){
#ifdef TRACKING_PROGRAM_STEPS
	cout << "TRACKING_PROGRAM_STEPS: EBFV1_preprocessor_2D\tIN\n";
#endif

	GeomData *pGCData = (GeomData*)pData;
	pGCData->setMeshDim(theMesh->getDim());

	theMesh->modifyState(2,1);	// create edge data structure
	theMesh->modifyState(0,2);	// create adjacency around nodes
	theMesh->modifyState(1,2);	// create faces around edge

	//cout << "Mesh dimension: 2D\n";
	int i,j,flag,dom;
	double edgeCenter[3], I[3], J[3];

	std::vector<double> Cij(3), Dij(3);
	pEntity edge, face, node;

	/// initialize coefficients
	initializeCoefficients(theMesh,pGCData);

	std::set<int> setOfDomain;

	// for each face, calculate all data needed and attach them to the its edges
	FIter fit = M_faceIter(theMesh);
	while ( (face = FIter_next(fit)) ){
		// look up only for leave elements (without children)
		if ( !theMesh->getRefinementDepth(face) ){
			dom = EN_getFlag(face);
			char dom_string[256]; sprintf(dom_string,"dom_str_%d",dom);

			if (!dom){
				throw Exception(__LINE__,__FILE__,"dom = 0\n");
			}

			setOfDomain.insert(dom);		// store every new domain
			double faceCenter[3] = {.0, .0, .0};
			getFCenter(face,faceCenter);

			// loop over all three face's edges to calculate Cij and Dij
			for (i=0; i<3; i++){
				edge = F_edge(face, i);

				// set edge belonging to domain 'dom'
				EN_attachDataInt(edge,MD_lookupMeshDataId( dom_string ),1);

				edgeCenter[0] = edgeCenter[1] = .0;
				E_center(edge,edgeCenter);				// edge centroid
				V_coord(edge->get(0,0),I);
				V_coord(edge->get(0,1),J);

				int id0 = EN_id(edge->get(0,0));
				int id1 = EN_id(edge->get(0,1));

				// edge vector, used as a reference vector
				double IJ[2] = {J[0]-I[0], J[1]-I[1]};

				// vector IJ must point from the smaller vertex ID to the greater
				if ( id0 > id1 ){
					for (j=0; j<2; j++){
						IJ[j] = -IJ[j];
					}
				}

				// vector: from element center to edge middle point, used as a reference vector
				double v[2] = {edgeCenter[0]-faceCenter[0],edgeCenter[1]-faceCenter[1]};

				// Cij is orthogonal to v
				for (j=0; j<2; j++){
					Cij[j] = .0;
				}

				// Cij must point as if I inside the CV and J outside
				double innerprod = v[1]*IJ[0] + (-v[0])*IJ[1];
				if ( innerprod <= .0 ){
					for (j=0; j<2; j++){
						v[j] = -v[j];
					}
				}

				// associate Cij coefficient to edge
				pGCData->getCij(edge,dom,Cij);
				Cij[0] += v[1];
				Cij[1] += -v[0];

				pGCData->setCij(edge,dom,Cij);
				//cout << "Cij = " << Cij[0] << "\t" << Cij[1] << "\t" << Cij[2] << "\n";
#ifdef __ADAPTATION_DEBUG__
		if (Cij[0]==.0 && Cij[1]==.0){
			char msg[256]; sprintf(msg,"Edge %d-%d has Cij coefficient NULL Cij[0]=Cij[1]=0.0",id0,id1);
			throw Exception(__LINE__,__FILE__,msg);
		}
#endif
			}

			// calculate volume of control volume and associate it to elements nodes
			const double porosity = .0;
			double A = F_area(face)/3.0;	// element area
			//cout << "area = " << A << endl;
			for (j=0; j<3; j++){
				node = face->get(0,j);
				EN_attachDataInt(node,MD_lookupMeshDataId( dom_string ),1);
				double v1 = pGCData->getVolume(node,dom);
				double v2 = pGCData->getWeightedVolume(node);
				v1 += A;
				v2 += A*porosity;
				pGCData->setVolume(node,dom,v1);
				pGCData->setWeightedVolume(node,v2);
			}
		}
	}
	FIter_delete(fit);

	int numBE = 0;
	// Calculate Dij coefficient only for boundary edges.
	EIter eit = M_edgeIter(theMesh);
	while ( (edge = EIter_next(eit)) ){
		if ( !theMesh->getRefinementDepth(edge) ){
 			flag = EN_getFlag(edge);
 			std::set<int>::iterator iter = setOfDomain.find( flag );
 			if (iter == setOfDomain.end()){		// this line is for general (h**o/hetero) adaptation
				numBE++;
				double I[3] = {.0,.0,.0}, J[3] = {.0,.0,.0};
				V_coord(edge->get(0,0),I);
				V_coord(edge->get(0,1),J);

				// Dij vector is orthogonal to edge (it's unknown Dij orientation)
				Dij[0] = -(J[1]-I[1])/2.0;
				Dij[1] =  (J[0]-I[0])/2.0;

				// make Dij points to outside domain. First, take face that uses edge and its flag
				int domains[2] = {0,0};
				for (i=0; i<E_numFaces(edge); i++){
					face = E_face(edge,i);
					if (!face){
						throw Exception(__LINE__,__FILE__,"Null face!\n");
					}
					domains[i] = EN_getFlag(face);
				}
				// that the reference face to make Dij points to outside
				face = E_face(edge, 0);

				if (!face){
					throw Exception(__LINE__,__FILE__,"Null face!\n");
				}

				// now, get face's center and edge's center
				double faceCenter[3] = {.0, .0, .0}, edgeCenter[3] = {.0, .0, .0};
				getFCenter(face,faceCenter);	// element centroid
				E_center(edge,edgeCenter);		// edge centroid

				// vector: from element center to edge middle point, used as a reference vector
				double v[2] = {edgeCenter[0]-faceCenter[0],edgeCenter[1]-faceCenter[1]};

				// Dij must point to outside element
				double innerprod = Dij[0]*v[0] + Dij[1]*v[1];
				if (  innerprod <= .0 ){
					for (j=0; j<2; j++){
						Dij[j] = -Dij[j];
					}
				}
				// associate to edge domains flags to wjich it belongs
				EN_attachDataInt(edge,MD_lookupMeshDataId("dom1"),domains[0]);
				EN_attachDataInt(edge,MD_lookupMeshDataId("dom1"),domains[1]);
				pGCData->setDij(edge,domains[0],domains[1],Dij);
			}
		}
	}
	EIter_delete(eit);
	//cout << "preprocessor: Number of Boundary edges = " << numBE << endl;
	calculateEdgeLength(theMesh,pGCData);
	calculateCijNorm(theMesh,pGCData,setOfDomain);

	// For 2-D domains, multiply volume by reservoir height (H) for 2D/3D simulations (physics occurs only on 2-D but reservoir volume is considered)
	double vt = .0;
	double vol,wvol;
	std::set<int>::iterator iter = setOfDomain.begin();
	for(;iter!=setOfDomain.end();iter++){
		VIter vit = M_vertexIter(theMesh);
		while (pEntity node = VIter_next(vit)){
			// do not divide vol by number of remote copies!
			vol = pGCData->getVolume(node,*iter);
			vt += vol;
			wvol = 0.2*vol;
			pGCData->setWeightedVolume(node,wvol);
		}
		VIter_delete(vit);
	}
	pGCData->setTotalReservoirVolume(vt);
	//cout << "Number of domains: " << setOfDomain.size() << ". They are: ";
//	for( iter = setOfDomain.begin(); iter!=setOfDomain.end(); iter++){
//		cout << *iter << "  ";
//	}
//	cout << endl;

	validete_coefficients(theMesh,setOfDomain,pGCData);

#ifdef TRACKING_PROGRAM_STEPS
	cout << "TRACKING_PROGRAM_STEPS: EBFV1_preprocessor_2D\tOUT\n";
#endif
	return 0;
}
void Cterrain::showAndMark(int jmin,int jmax,int imin,int imax,int curDepth)
{
    //检查当前节点是否与视截体相交
    //求节点p所表示区域的保守包围盒
    //    上面
    //    p[0]--p[3]
    //     |     |
    //    p[1]--p[2]
    //    下面
    //    p[4]--p[7]
    //     |     |
    //    p[5]--p[6]
    float xmin=m_range.getMinX()+gridSize*jmin;
    float xmax=m_range.getMinX()+gridSize*jmax;
    float zmin=m_range.getMinZ()+gridSize*imin;
    float zmax=m_range.getMinZ()+gridSize*imax;
    float ymin=m_range.getMinY();
    float ymax=m_range.getMaxY();
    float c[3]={(xmax+xmin)/2,(ymin+ymax)/2,(zmin+zmax)/2};
    float r=max(xmax-xmin,ymax-ymin)*0.86602540378443864676372317075294;//由于zmax-zmin与xmax-xmin相等,所以不用考虑
    //看球体(c,r)是否都在planeList中某个面的反面,如果是则可剔除
    bool visible=true;
    for(int i=0;i<5;i++){//不考虑远平面
        const Cplane&plane=this->getModel()->getMeshByIndex(0)->getCamera()->getFrustum().getPlaneByIndex(i);
        //看球体(c,r)是否在plane的背面
        float PND=PND_point_plane(plane, c);
        if(PND<-r){//如果在背面
            //断定为不可见,不用再继续检测
            visible=false;
            break;
        }
    }//得到visible
    if(visible){//如果可见
        bool needDiv=false;//是否需要再分
        //求needDiv
        if(imin+1==imax){//无须再分,因为已经无法再分
            needDiv=false;
        }else{//进一步判断
            //求c到视点的距离
            //指数值越大,LOD效应越明显
            float d=square(this->getModel()->getMeshByIndex(0)->getCamera()->getEyePos().x()-c[0])
            +square(minf(0.6*(this->getModel()->getMeshByIndex(0)->getCamera()->getEyePos().y()-c[1]),700))//Y乘以一个比1小的系数是为了让LOD对高度变化不敏感
            +square(this->getModel()->getMeshByIndex(0)->getCamera()->getEyePos().z()-c[2]);
            float e=xmax-xmin;//边长
            if(d<e*reso)needDiv=true;
        }//得到needDiv
        if(needDiv){//继续分
            int imid=(imin+imax)>>1;//除2
            int jmid=(jmin+jmax)>>1;
            markmat[imid][jmid]=true;
            markedElementIndexList.push_back(Cij(imid,jmid));
            //对四个孩子继续递归
            showAndMark(jmin,jmid,imin,imid,curDepth+1);
            showAndMark(jmin,jmid,imid,imax,curDepth+1);
            showAndMark(jmid,jmax,imid,imax,curDepth+1);
            showAndMark(jmid,jmax,imin,imid,curDepth+1);
        }else{//不分
            const int vIDMat_imin=(int)markmat[0].size()*imin;//vIDMat[imin];
            const int vIDMat_imax=(int)markmat[0].size()*imax;//vIDMat[imax];
            const int ID0=vIDMat_imin+jmin;
            const int ID1=vIDMat_imax+jmin;
            const int ID2=vIDMat_imax+jmax;
            const int ID3=vIDMat_imin+jmax;
            this->getModel()->getMeshByIndex(0)->addIDtri(ID0, ID1, ID2);
            this->getModel()->getMeshByIndex(0)->addIDtri(ID0, ID2, ID3);
        }
    }
Esempio n. 5
0
void Cterrain::getBlocks(int jmin,int jmax,int imin,int imax,int curDepth)
{
    //检查当前节点是否与视截体相交--abc
    //求节点p所表示区域的保守包围盒--abc
    //    上面--abc
    //    p[0]--p[3]
    //     |     |
    //    p[1]--p[2]
    //    下面--abc
    //    p[4]--p[7]
    //     |     |
    //    p[5]--p[6]
    float xmin=m_range.getMinX()+gridSize*jmin;
    float xmax=m_range.getMinX()+gridSize*jmax;
    float zmin=m_range.getMinZ()+gridSize*imin;
    float zmax=m_range.getMinZ()+gridSize*imax;
    float ymin=m_range.getMinY();
    float ymax=m_range.getMaxY();
    float c[3]={(xmax+xmin)/2,(ymin+ymax)/2,(zmin+zmax)/2};
    float r=max(xmax-xmin,ymax-ymin)*0.86602540378443864676372317075294;//由于zmax-zmin与xmax-xmin相等,所以不用考虑--abc
    //看球体(c,r)是否都在planeList中某个面的反面,如果是则可剔除--abc
    bool visible=true;
    for(int i=0;i<5;i++){//不考虑远平面--abc
        const Cc3dPlane&plane=this->getMesh()->getSubMeshByIndex(0)->getCamera()->getFrustum().getPlaneByIndex(i);
        //看球体(c,r)是否在plane的背面--abc
        float PND=directedDistanceFromPointToPlane(plane, c);
        if(PND<-r){//如果在背面--abc
            //断定为不可见,不用再继续检测--abc
            visible=false;
            break;
        }
    }//得到visible
    if(visible){//如果可见--abc
        bool needDiv=false;//是否需要再分--abc
        //求needDiv
        if(imin+1==imax){//无须再分,因为已经无法再分--abc
            needDiv=false;
        }else{//进一步判断--abc
            //求c到视点的距离--abc
            float d2=square(this->getMesh()->getSubMeshByIndex(0)->getCamera()->getEyePos().x()-c[0])
         ///   +square(this->getMesh()->getSubMeshByIndex(0)->getCamera()->getEyePos().y()-c[1])
            +square(this->getMesh()->getSubMeshByIndex(0)->getCamera()->getEyePos().z()-c[2]);
            float e=xmax-xmin;//边长--abc
            if(d2<square(e*reso))needDiv=true;
        }//得到needDiv
        if(needDiv){//继续分--abc
            int imid=(imin+imax)>>1;//除2
            int jmid=(jmin+jmax)>>1;
            markmat[imid][jmid]=true;
            markedElementIndexList.push_back(Cij(imid,jmid));
            //对四个孩子继续递归--abc
            getBlocks(jmin,jmid,imin,imid,curDepth+1);
            getBlocks(jmin,jmid,imid,imax,curDepth+1);
            getBlocks(jmid,jmax,imid,imax,curDepth+1);
            getBlocks(jmid,jmax,imin,imid,curDepth+1);
        }else{//不分--abc
			CterrainBlock block(imin,imax,jmin,jmax);
			m_blockList.push_back(block);
   
        }
    }
Esempio n. 6
0
void validete_coefficients(pMesh theMesh, std::set<int>& setOfDomain, GeomData* pGCData){

#ifdef TRACKING_PROGRAM_STEPS
	cout << "TRACKING_PROGRAM_STEPS: validate pre-processor coefficient calculation\tIN\n";
#endif


	cout << setprecision(8) << scientific;
	pEntity edge;
	std::vector<double> Cij(3), Dij(3);
	std::map<int,double*> coeff_sum;

	std::set<int>::iterator iter = setOfDomain.begin();

	for(;iter!=setOfDomain.end(); iter++){
		int dom = *iter;

		// initialize all nodes for domain "dom"
		double* p;
		VIter vit = M_vertexIter(theMesh);
		while (pEntity node = VIter_next(vit)){
			p = new double[3];
			p[0] = p[1] = p[2] = .0;
			coeff_sum[EN_id(node)] = p;
		}
		VIter_delete(vit);


		// calculate summation of coefficients
		EIter eit = M_edgeIter(theMesh);
		while ( (edge = EIter_next(eit)) ){
			int I = EN_id(edge->get(0,0));
			int J = EN_id(edge->get(0,1));
			pGCData->getCij(edge,dom,Cij);
			double sign = (I>J)?-1.0:1.0;


			p = coeff_sum[I];
			p[0] += sign*Cij[0];
			p[1] += sign*Cij[1];
			p[2] += sign*Cij[2];
			if (pGCData->getDij(edge,dom,Dij)){
				p[0] += Dij[0];
				p[1] += Dij[1];
				p[2] += Dij[2];
			}

			p = coeff_sum[J];
			p[0] += -sign*Cij[0];
			p[1] += -sign*Cij[1];
			p[2] += -sign*Cij[2];
			if (pGCData->getDij(edge,dom,Dij)){
				p[0] += Dij[0];
				p[1] += Dij[1];
				p[2] += Dij[2];
			}
		}
		EIter_delete(eit);

		double summation[3] = {.0,.0,.0};
		cout << "Domain: " << dom << endl;
		//cout << "ID:\t" << "Summ:\n";
		std::map<int,double*>::iterator miter = coeff_sum.begin();
		for(;miter!=coeff_sum.end();miter++){
			//int ID = miter->first;
			p = miter->second;
			//cout << ID << "\t" << p[0] << " " << p[1] << " " << p[2] << endl;
			summation[0] += p[0];
			summation[1] += p[1];
			summation[2] += p[2];
			dealloc_DOUBLE_vector(p);
		}
		cout <<  "\n\nSummation of all summations (Cij/Dij):  " << summation[0] << " " << summation[1] << " " << summation[2] << "\n";

		double vol_control = 0;
		vit = M_vertexIter(theMesh);
		while (pEntity node = VIter_next(vit)){
			vol_control += pGCData->getVolume(node,dom);
		}
		VIter_delete(vit);
		cout <<  "Total Volume                         :  " << vol_control << "\n\n\n";

	}
#ifdef TRACKING_PROGRAM_STEPS
	cout << "TRACKING_PROGRAM_STEPS: validate pre-processor coefficient calculation\tOUT\n";
#endif

}
Esempio n. 7
0
// Calculates Cij, Dij(boundary faces) and nodal volume. It also works for multi domains meshes
int EBFV1_preprocessor_3D(pMesh theMesh, void *pData, int &ndom){
	PetscPrintf(PETSC_COMM_WORLD,"Starting EBFV1-3D pre-processor...");

	GeomData *pGCData = (GeomData*)pData;
	pGCData->setMeshDim(theMesh->getDim());
	if (theMesh->getDim() != 3){
		throw Exception(__LINE__,__FILE__,"Only 3-D meshes are allowed. Exiting...\n");
	}

	theMesh->modifyState(3,2);
	theMesh->modifyState(2,3);

	pEntity tetra, edge, face;
	int i,j,k,K,m;
	double tCenter[3], eCenter[3], v[3], *vec[2], val, *tetraFCenter[4];
	double normal[3], I[3], J[3], IJ[3], proj[3];

	// allocate vectors
	for (i=0; i<4; i++) tetraFCenter[i] = new double[3];
	for (i=0; i<2; i++) vec[i] = new double[3];

	// Search over all tetrahedra flags set to define domains. Store all flags on a list and initialize Cij vector
	std::set<int> setOfDomain;
	std::vector<double> Cij(3,.0);
	RIter rit = M_regionIter(theMesh);
	while ( (tetra = RIter_next(rit)) ){
		int flag = GEN_tag(tetra->getClassification());
		setOfDomain.insert( flag );
		for (i=0; i<6; i++){
			edge = (pEntity)tetra->get(1,i);
			pGCData->setCij(edge,flag,Cij);
		}
	}
	RIter_delete(rit);

	// mark faces (boundary)
	bool detectBdryFaces = false;
	std::set<int>::iterator iter = setOfDomain.begin();
	for (; iter != setOfDomain.end(); iter++){
		int countBDRYFaces = 0;
		int dom = *iter;
		char dom_string[256]; sprintf(dom_string,"dom_str_%d",dom);
		RIter rit = M_regionIter(theMesh);
		while ( (tetra = RIter_next(rit)) ){
			int tetraflag = GEN_tag(tetra->getClassification());
			if (tetraflag==dom){
				for (i=0;i<4;i++){
					face = (pFace)tetra->get(2,i);
					int faceflag = GEN_tag(face->getClassification());
					// if flags are different, then face in on boundary
					if (faceflag!=tetraflag){
						detectBdryFaces = true;
						countBDRYFaces++;
						// set edge belonging to domain 'dom'
						EN_attachDataInt(face,MD_lookupMeshDataId( dom_string ),1);
					}
				}
			}
		}
		RIter_delete(rit);
	}
	//throw 1;

	if (!detectBdryFaces){
		throw Exception(__LINE__,__FILE__,"Any boundary face (triangles) were detected. Boundary elements MUST have different flag of internal elements.");
	}

	/// initialize weighted volume
	VIter vit = M_vertexIter(theMesh);
	while (pEntity v = VIter_next(vit))
		pGCData->setWeightedVolume(v,0.0);
	VIter_delete(vit);

	vector<pVertex> tetraVertex(4), edgeVertex(2);
	double vt = .0; // total volume

	// loop over elements
	// for each element:	1 - calculate Cij for each edge and store them on the respective edge
	//						2 - calculate element contribution to volume of control volume
//	PetscPrintf(PETSC_COMM_WORLD,"Loop over tetras - start...  ");MPI_Barrier(MPI_COMM_WORLD);
	rit = M_regionIter(theMesh);
	while ( (tetra = RIter_next(rit)) ){
		// get all four tetrahedron's vertices
		for (i=0; i<4; i++){
			tetraVertex[i] = (pEntity)tetra->get(0,i);
		}

		int tetraFaces[4][3] = { {EN_id(tetraVertex[0]),EN_id(tetraVertex[1]),EN_id(tetraVertex[2])},
				{EN_id(tetraVertex[0]),EN_id(tetraVertex[1]),EN_id(tetraVertex[3])},
				{EN_id(tetraVertex[0]),EN_id(tetraVertex[2]),EN_id(tetraVertex[3])},
				{EN_id(tetraVertex[1]),EN_id(tetraVertex[2]),EN_id(tetraVertex[3])}};

		getFCenter(tetraVertex[0], tetraVertex[1], tetraVertex[2], tetraFCenter[0]);
		getFCenter(tetraVertex[0], tetraVertex[1], tetraVertex[3], tetraFCenter[1]);
		getFCenter(tetraVertex[0], tetraVertex[2], tetraVertex[3], tetraFCenter[2]);
		getFCenter(tetraVertex[1], tetraVertex[2], tetraVertex[3], tetraFCenter[3]);

		// get tetrahedron center
		tCenter[0] = tCenter[1] = tCenter[2] = .0;
		R_center(tetra,tCenter);

		double coord1[3]; V_coord(tetraVertex[0],coord1);
		double coord2[3]; V_coord(tetraVertex[1],coord2);
		double coord3[3]; V_coord(tetraVertex[2],coord3);
		double coord4[3]; V_coord(tetraVertex[3],coord4);

		// step #1: Cij
		// identify which domain the current tetrahedron belongs to
		// edges on domain's partition have differents Cij, one for each domain
		const int dom = GEN_tag(tetra->getClassification());
		char dom_string[256]; sprintf(dom_string,"dom_str_%d",dom);
		markTetraBdryFaces(theMesh,tetra,dom);
		setOfDomain.insert(dom); // store every new domain

		for (int pos1=0; pos1<3; pos1++){
			for (int pos2=pos1+1; pos2<4; pos2++){
				edge = (pEdge)theMesh->getEdge((mVertex*)tetraVertex[pos1],(mVertex*)tetraVertex[pos2]);

				// set edge belonging to domain 'dom'
				EN_attachDataInt(edge,MD_lookupMeshDataId( dom_string ),1);


				//M_GetVertices(edge,edgeVertex);
				for (i=0; i<2; i++) edgeVertex[i] = (pEntity)edge->get(0,i);
				// edge's Cij is the sum of two vectors. Both are orthogonals to planes
				// defined by three vectors. They are:
				// v - ec->tc:		edge center to tetra center
				// vec[0] - ec->fcr:	edge center to right face center
				// vec[1] - ec->fcl:	edge center to left face center
				// Cij = (v)x(vec[0]) + (v)x(vec[1]), Cij points to outside of control volume

				// create vector v:
				for (i=0; i<3; i++) I[i] = J[i]= .0;
				V_coord(edgeVertex[0],I);
				V_coord(edgeVertex[1],J);

				double sign = ( EN_id(edgeVertex[0]) > EN_id(edgeVertex[1]) )?-1.0:1.0;
				for (i=0; i<3; i++){
					IJ[i] = sign*(J[i] - I[i]);
				}

				// get edge center
				eCenter[0] = eCenter[1] = eCenter[2] = .0;
				for (i=0; i<3; i++){
					eCenter[i] = .5*(I[i]+J[i]);
				}

				// create vector v: from edge middle point to tetrahedral centroid
				for (i=0; i<3; i++){
					v[i] = tCenter[i] - eCenter[i];
				}

				// search for tetra faces that share the same edge and get their centers. Of course, there are only two faces
				// sharing the same edge. FMDB can do this job easily, but for 3D big meshes a face structure has a high memory cost.

				K = 0;
				// loop over tetra's faces
				for (i=0; i<4; i++){
					// loop over face's vertices
					for (j=0; j<3; j++){
						if (EN_id(edgeVertex[0]) == tetraFaces[i][j]){
							for (k=0; k<3; k++){
								if (EN_id(edgeVertex[1])==tetraFaces[i][k]){
									for (m=0; m<3; m++) vec[K][m] = -eCenter[m] + tetraFCenter[i][m];
									K++;
								}
							}
						}
					}
				}

				// IJ vector is a reference to Cij. IJ points from node I to node J
				// where: I_id < J_id
				// vec projections on edge IJ must have the same orientation as IJ vector
				double n = sqrt( IJ[0]*IJ[0] + IJ[1]*IJ[1] + IJ[2]*IJ[2] ) ;

				// Cij calculation n ;
				for (j=0; j<3; j++){
					Cij[j] = .0;
				}
				pGCData->getCij(edge,dom,Cij);
				double normal1[3], normal2[3];
				cross(v,vec[0],normal1);
				cross(vec[1],v,normal2);

				for (j=0; j<3; j++){
					proj[j] = .0;
				}
				for (j=0; j<3; j++){
					normal[j] = .5*(normal1[j]+normal2[j]);
				}
				val = dot(normal,IJ)/(n*n);
				for (j=0; j<3; j++){
					proj[j] = val*IJ[j];
				}
				double sinal = (dot(proj,IJ)<.0)?-1.:1.;
				for (j=0; j<3; j++){
					Cij[j] += sinal*normal[j];
				}
				pGCData->setCij(edge,dom,Cij);
			}
		}

		// step #2: volume of control volume
		const double porosity = .0;
		const double tetraVolume = R_Volume(tetra);
		const double nodalVolume = .25*tetraVolume;
		vt = +tetraVolume;

		for (i=0; i<4; i++){
			// total nodal volume
			double v1 = pGCData->getVolume(tetraVertex[i],dom);
			v1 += nodalVolume;
			pGCData->setVolume(tetraVertex[i],dom,v1);
			double v2 = pGCData->getWeightedVolume(tetraVertex[i]);
			v2 += nodalVolume*porosity;
			pGCData->setWeightedVolume(tetraVertex[i],v2);
		}
	}
	RIter_delete(rit);	// END TETRAHEDRALS LOOP
	pGCData->setTotalReservoirVolume(vt);
	//PetscPrintf(PETSC_COMM_WORLD,"Finished\n");MPI_Barrier(MPI_COMM_WORLD);

	// deallocate vectors
	for (i=0; i<4; i++) delete[] tetraFCenter[i];
	for (i=0; i<2; i++) delete[] vec[i];

	/*
	 * Dij vector calculation. A loop over all boundary (external/internal) faces is made. Depending on how FMDB is used, faces on all
	 * tetrahedrals may be created and they must be filtered. If a face does not belong to boundary it will assume the tetrahedral domain
	 * flag. This value is greater than 3000 and is used to filter them from those provided by mesh file.
	 */

	int count = 0;
	dblarray Dij(3,.0);
	//PetscPrintf(PETSC_COMM_WORLD,"Loop over boundary faces - start... ");///MPI_Barrier(MPI_COMM_WORLD);
	if ( M_numFaces(theMesh) != 0 ){
		FIter fit = M_faceIter(theMesh);
		while ( (face = FIter_next(fit)) ){
			int flag = GEN_tag(face->getClassification());
			iter = setOfDomain.find(flag);
			// get only faces built over surfaces defined by user in geometric model
			// it only work if surface flags defined by user are different from those set to tetrahedrals
			if ( iter==setOfDomain.end() ){
				computeDij(theMesh,face,pGCData);
			}
		}
		FIter_delete(fit);
	}
	//PetscPrintf(PETSC_COMM_WORLD,"Finished\n");MPI_Barrier(MPI_COMM_WORLD);
	calculateEdgeLength(theMesh,pGCData);
	calculateCijNorm(theMesh,pGCData,setOfDomain);
	identifyBoundaryElements(theMesh,pGCData,setOfDomain);
	i = 0;
	ndom = (int)setOfDomain.size();
	int *domlist = new int[ndom];
	for(iter = setOfDomain.begin(); iter!=setOfDomain.end(); iter++){
		domlist[i++] =  *iter;
	}

	PetscPrintf(PETSC_COMM_WORLD," finished.\n");
	cout << "-------------------------------------------------------------------------------------------------------------------------\n\n";
	return 0;
}