Beispiel #1
0
std::vector<XYZ> Gauss::Integrate(const ValueField values)
{
  // Pre- condition
  int m = values.size();
  int n = values[0].size();
  assert( m == m_ && n == n_ );

  // define
  std::vector<XYZ> force(num_facenodes_);
  
  // normal
  XYZ normal = Func::GetNormalOf(face_);
  // area
  double area = std::sqrt( normal * normal );
  // normalization
  normal = normal / std::sqrt( normal * normal );

  for(int inode=0; inode<num_facenodes_; inode++){
    for(int i=0; i<m; i++){
      for(int j=0; j<n; j++){
	XYZ tmp;
	tmp = -normal * area * 0.25 * Wi_[i] * Wj_[j];
	tmp = tmp * values[i][j];
	tmp = tmp * ShapeFunction(inode, xi_[i], et_[j]);
	force.at(inode) += tmp;
      }
    }
  }
  return force;
}
Beispiel #2
0
// Take stress at 9 gauss points and map them to 9 nodes in this element
// by using coordinate system on the gauss points (numbered as element as
//		1,7,9,3,4,8,6,2,5) as mapping to -1 to 1 coordinates.
// sgp[i][j] is stress j (1 to 4) at Gauss point i (1 to numGauss)
// se[i][j] is output stress j (1 to 4) at node i (1 to numnds) (externed variable)
void Lagrange2D::ExtrapolateGaussStressToNodes(double sgp[][5])
{
	double gpt = 1./0.7745966692414834;
	double temp,sfxn[10];
	int i,j,k;
	Vector xi;
	
	// loop over 9 nodes
	for(i=1;i<=9;i++)
	{	if(i==1 || i==8 || i==4)
			xi.x = -gpt;
		else if(i==5 || i==9 || i==7)
			xi.x = 0.;
		else
			xi.x = gpt;
		if(i==1 || i==5 || i==2)
			xi.y = -gpt;
		else if(i==8 || i==9 || i==6)
			xi.y = 0.;
		else
			xi.y = gpt;
		
		ShapeFunction(&xi,FALSE,sfxn,NULL,NULL,NULL,NULL,NULL,NULL);
		for(j=1;j<=4;j++)
		{	temp=0.;
			for(k=0;k<9;k++)
				temp+=sfxn[k]*sgp[gaussOrder[k]][j];
			se[i][j] = temp;
		}
	}
}
void ElementBase::GetShapeFunctionsForCracks(int *numnds,double *fn,int *nds,Vector *pos) const
{
#ifdef CRACK_POINT
	Vector xipos;
	GetNodes(numnds,nds);
	GetXiPos(pos,&xipos);
	ShapeFunction(&xipos,FALSE,&fn[1],NULL,NULL,NULL);
#else
	Vector xipos,lp;
	int ndIDs[maxShapeNodes];
	
    switch(useGimp)
    {   case POINT_GIMP:
        	// Load element noodes, dimensionless position, and shape functinos
            GetNodes(numnds,nds);
            GetXiPos(pos,&xipos);
            ShapeFunction(&xipos,FALSE,&fn[1],NULL,NULL,NULL);
            break;
            
        case LINEAR_CPDI:
        case QUADRATIC_CPDI:
			// since no material point, CPDI uses GIMP method
        case UNIFORM_GIMP:
            GetXiPos(pos,&xipos);
            lp.x = mpmgrid.GetParticleSemiLength();
            lp.y = lp.x;
            lp.z = lp.x;
            GetGimpNodes(numnds,nds,ndIDs,&xipos,lp);
            GimpShapeFunction(&xipos,*numnds,ndIDs,FALSE,&fn[1],NULL,NULL,NULL,lp);
            GimpCompact(numnds,nds,fn,NULL,NULL,NULL);
            break;
            
		case LINEAR_CPDI_AS:
			// since no material point, CPDI uses GIMP method
        case UNIFORM_GIMP_AS:
            GetXiPos(pos,&xipos);
            lp.x = mpmgrid.GetParticleSemiLength();
            lp.y = lp.x;
            lp.z = lp.x;
            GetGimpNodes(numnds,nds,ndIDs,&xipos,lp);
            GimpShapeFunctionAS(&xipos,*numnds,ndIDs,FALSE,&fn[1],NULL,NULL,NULL,lp);
            GimpCompact(numnds,nds,fn,NULL,NULL,NULL);
            break;
     }
#endif
}
/* Just get nodes and shape functions
	Load number of nodes into numnds
	Load node numbers into nds[1]...
	Load shape functions into fn[1]...
	See other GetShapeFunctions() if need to change
 NOTE: This is called at various places in the time step when shape functions are needed. It should
	recalculate the ones found at the begnning of the time step using precalculated xipos
    or CPDI info, which are found in initialization
 throws CommonException() if too many CPDI nodes
*/
void ElementBase::GetShapeFunctions(int *numnds,double *fn,int *nds,MPMBase *mpmptr) const
{
    Vector lp;
    
    switch(useGimp)
    {   case POINT_GIMP:
        	// load coordinates if not already done
            GetNodes(numnds,nds);
            ShapeFunction(mpmptr->GetNcpos(),FALSE,&fn[1],NULL,NULL,NULL);
            break;
            
        case UNIFORM_GIMP:
        {	// GIMP analysis
            int ndIDs[maxShapeNodes];
            Vector *xipos = mpmptr->GetNcpos();
            mpmptr->GetDimensionlessSize(lp);
            GetGimpNodes(numnds,nds,ndIDs,xipos,lp);
            GimpShapeFunction(xipos,*numnds,ndIDs,FALSE,&fn[1],NULL,NULL,NULL,lp);
            GimpCompact(numnds,nds,fn,NULL,NULL,NULL);
            break;
        }
            
        case UNIFORM_GIMP_AS:
        {	// GIMP analysis
            int ndIDs[maxShapeNodes];
            Vector *xipos = mpmptr->GetNcpos();
            mpmptr->GetDimensionlessSize(lp);
            GetGimpNodes(numnds,nds,ndIDs,xipos,lp);
            GimpShapeFunctionAS(xipos,*numnds,ndIDs,FALSE,&fn[1],NULL,NULL,NULL,lp);
            GimpCompact(numnds,nds,fn,NULL,NULL,NULL);
            break;
        }
            
        case LINEAR_CPDI:
		case LINEAR_CPDI_AS:
        case QUADRATIC_CPDI:
        {   if(theMaterials[mpmptr->MatID()]->Rigid())
            {	// GIMP analysis
                int ndIDs[maxShapeNodes];
                Vector *xipos = mpmptr->GetNcpos();
                mpmptr->GetDimensionlessSize(lp);
                GetGimpNodes(numnds,nds,ndIDs,xipos,lp);
                if(fmobj->IsAxisymmetric())
                    GimpShapeFunctionAS(xipos,*numnds,ndIDs,FALSE,&fn[1],NULL,NULL,NULL,lp);
                else
                    GimpShapeFunction(xipos,*numnds,ndIDs,FALSE,&fn[1],NULL,NULL,NULL,lp);
                GimpCompact(numnds,nds,fn,NULL,NULL,NULL);
            }
            else
            {   *numnds = GetCPDIFunctions(nds,fn,NULL,NULL,NULL,mpmptr);
            }
            break;
		}
    }
}
// Find Cartesion position from natural coordinates
void ElementBase::GetPosition(Vector *xipos,Vector *xyzpos)
{
	double fn[MaxElNd];
	ShapeFunction(xipos,FALSE,&fn[1],NULL,NULL,NULL);
	int i;
	ZeroVector(xyzpos);
	for(i=1;i<=NumberNodes();i++)
	{	xyzpos->x += fn[i]*nd[nodes[i-1]]->x;
		xyzpos->y += fn[i]*nd[nodes[i-1]]->y;
		xyzpos->z += fn[i]*nd[nodes[i-1]]->z;
	}
}
Beispiel #6
0
XYZ Gauss::GetGaussPoint(const int i, const int j)
{
  // Pre- condition
  assert( i > -1 && j > -1 );
  assert( i < m_ && j < n_ );

  XYZ grid(0,0,0);

  for(int inode=0; inode<num_facenodes_; inode++){
    grid += face_.nodes(inode).grid() * ShapeFunction(inode, xi_[i], et_[j]);
  }
  
  return grid;
}
/* Find dimensionless coordinates by numerical methods
	input: pos is position in the element
	output: xipos is dimensionless position
	only used in MPM and only here if non-rectangular elements
*/
void ElementBase::GetXiPos(Vector *pos,Vector *xipos) const
{
    double xt,yt,dxxi,dxeta,dyxi,dyeta;
    double deter,dxi,deta,dist;
    double gfn[MaxElNd],gdfnxi[MaxElNd],gdfnet[MaxElNd];
    int numnds=NumberNodes(),i,j;
	
	// initial guess
	GetCentroid(xipos);
	
	// nodal coordinates
	Vector eNode[MaxElNd];
	for(i=0;i<numnds;i++)
	{	eNode[i].x=nd[nodes[i]]->x;
		eNode[i].y=nd[nodes[i]]->y;
	}
    
    /* solve for xipos using Newton-Rapheson (see FEA Notes)
            using shape functions and their derivatives */
    for(j=1;j<=MAXITER;j++)
    {   ShapeFunction(xipos,TRUE,gfn,gdfnxi,gdfnet,NULL,NULL,NULL,NULL);
        xt=-pos->x;
        yt=-pos->y;
        dxxi=0.;
        dxeta=0.;
        dyxi=0.;
        dyeta=0.;
        for(i=0;i<numnds;i++)
        {   xt+=eNode[i].x*gfn[i];
            yt+=eNode[i].y*gfn[i];
            dxxi+=eNode[i].x*gdfnxi[i];
            dxeta+=eNode[i].x*gdfnet[i];
            dyxi+=eNode[i].y*gdfnxi[i];
            dyeta+=eNode[i].y*gdfnet[i];
        }
        deter=dxxi*dyeta-dxeta*dyxi;
        dxi=(-xt*dyeta+yt*dxeta)/deter;
        deta=(xt*dyxi-yt*dxxi)/deter;
        xipos->x+=dxi;
        xipos->y+=deta;
        dist=sqrt(dxi*dxi+deta*deta);
        if(dist<.001) break;
    }
}
/* Calculate Stiffness Matrix
*/
void Interface2D::Stiffness(int np)
{
	double fn[MaxElNd],temp,asr;
	Vector xi;
	
    // Load nodal coordinates (ce[]) and material props (pr.C[][])
    GetProperties(np);
    
    // Zero stiffness (se[][]) and reaction (re[])
	ZeroUpperHalfStiffness();

	// basic parameters */
    MaterialBase *matl=theMaterials[material-1];
	double Dn=matl->pr.C[1][1];
	double Dt=matl->pr.C[1][2];
	double xpxi=(ce[2].x-ce[1].x)/2;
	double ypxi=(ce[2].y-ce[1].y)/2;
	double dlxi=sqrt(xpxi*xpxi + ypxi*ypxi);
	
	int i;
	for(i=0;i<GAUSS_INT_PTS;i++)
	{	// get shape function at next xi
		xi.x=gxi[i];
		ShapeFunction(&xi,FALSE,&fn[1],NULL,NULL,&ce[1],NULL,&asr,NULL);
		
		// total weight
		if(np!=AXI_SYM)
			temp=gwt[i]*GetThickness();
		else
			temp=gwt[i]*asr;		// stiffness matrix is force per radian, hence no 2 pi
				
		// increment all stiffness elements at this point
		IncrementStiffnessElements(temp,fn,xpxi,ypxi,dlxi,Dn,Dt);
	}
		
	// Fill in lower half of stiffness matrix
	FillLowerHalfStiffness();
}
// by non-element methods that need access to grid shape functions only, and those methods are protected
void ElementBase::GridShapeFunctions(int *numnds,int *nds,Vector *xipos,double *fn) const
{
    GetNodes(numnds,nds);
    ShapeFunction(xipos,FALSE,&fn[1],NULL,NULL,NULL);
}
// return dimensionless location for material points
void ElementBase::MPMPoints(short numPerElement,Vector *mpos) const
{
    int i,j,k;
    double fxn[MaxElNd],row,zrow;
    
    if(NumberSides()==4)
    {	switch(numPerElement)
        {   case 4:
                // ENI or FNI - 2D only
                mpos[0].x=-.5;
                mpos[0].y=-.5;
                mpos[0].z=0.;
                mpos[1].x=.5;
                mpos[1].y=-.5;
                mpos[1].z=0.;
                mpos[2].x=-.5;
                mpos[2].y=.5;
                mpos[2].z=0.;
                mpos[3].x=.5;
                mpos[3].y=.5;
                mpos[3].z=0.;
                break;
            case 1:
                // CM of square or brick
                mpos[0].x=0.;
                mpos[0].y=0.;
				mpos[0].z=0.;
				break;
			case 8:
				// 3D box
                mpos[0].x=-.5;
                mpos[0].y=-.5;
				mpos[0].z=-.5;
                mpos[1].x=.5;
                mpos[1].y=-.5;
				mpos[1].z=-.5;
                mpos[2].x=-.5;
                mpos[2].y=.5;
				mpos[2].z=-.5;
                mpos[3].x=.5;
                mpos[3].y=.5;
				mpos[3].z=-.5;
                mpos[4].x=-.5;
                mpos[4].y=-.5;
				mpos[4].z=.5;
                mpos[5].x=.5;
                mpos[5].y=-.5;
				mpos[5].z=.5;
                mpos[6].x=-.5;
                mpos[6].y=.5;
				mpos[6].z=.5;
                mpos[7].x=.5;
                mpos[7].y=.5;
				mpos[7].z=.5;
				break;
            case 9:
                // 2D
                k=0;
                row = -2./3.;
                for(j=0;j<3;j++)
                {   mpos[k].x=-2./3.;
                    mpos[k].y=row;
                    mpos[k].z=0.;
                    mpos[k+1].x=0.;
                    mpos[k+1].y=row;
                    mpos[k+1].z=0.;
                    mpos[k+2].x=2./3.;
                    mpos[k+2].y=row;
                    mpos[k+2].z=0.;
                    k += 3;
                    row += 2./3.;
                }
                break;
            case 16:
                // 2D
                k=0;
                row = -0.75;
                for(j=0;j<4;j++)
                {   mpos[k].x=-0.75;
                    mpos[k].y=row;
                    mpos[k].z=0.;
                    mpos[k+1].x=-.25;
                    mpos[k+1].y=row;
                    mpos[k+1].z=0.;
                    mpos[k+2].x=.25;
                    mpos[k+2].y=row;
                    mpos[k+2].z=0.;
                    mpos[k+3].x=.75;
                    mpos[k+3].y=row;
                    mpos[k+3].z=0.;
                    k += 4;
                    row += 0.5;
                }
                break;
            case 25:
                // 2D
                k=0;
                row = -0.8;
                for(j=0;j<5;j++)
                {   mpos[k].x=-0.8;
                    mpos[k].y=row;
                    mpos[k].z=0.;
                    mpos[k+1].x=-.4;
                    mpos[k+1].y=row;
                    mpos[k+1].z=0.;
                    mpos[k+2].x=0.;
                    mpos[k+2].y=row;
                    mpos[k+2].z=0.;
                    mpos[k+3].x=.4;
                    mpos[k+3].y=row;
                    mpos[k+3].z=0.;
                    mpos[k+4].x=.8;
                    mpos[k+4].y=row;
                    mpos[k+4].z=0.;
                    k += 5;
                    row += 0.4;
                }
                break;
            case 27:
                // 3D
                k=0;
                zrow = -2./3.;
                for(i=0;i<3;i++)
                {   row = -2./3.;
                    for(j=0;j<3;j++)
                    {   mpos[k].x=-2./3.;
                        mpos[k].y=row;
                        mpos[k].z=zrow;
                        mpos[k+1].x=0.;
                        mpos[k+1].y=row;
                        mpos[k+1].z=zrow;
                        mpos[k+2].x=2./3.;
                        mpos[k+2].y=row;
                        mpos[k+2].z=zrow;
                        k += 3;
                        row += 2./3.;
                    }
                    zrow += 2./3.;
                }
                break;
            default:
                throw CommonException("Invalid number of material points per element.","ElementBase::MPMPoints");
                break;
        }
    }
    
    // covert to x-y-z locations
    for(k=0;k<numPerElement;k++)
    {	ShapeFunction(&mpos[k],FALSE,fxn,NULL,NULL,NULL);
		ZeroVector(&mpos[k]);
        for(j=0;j<NumberNodes();j++)
        {   mpos[k].x+=nd[nodes[j]]->x*fxn[j];
            mpos[k].y+=nd[nodes[j]]->y*fxn[j];
            mpos[k].z+=nd[nodes[j]]->z*fxn[j];
        }
    }
}
/* Do several element things at once
    Load number of nodes into numnds
    Load node numbers into nds[1]...
    Load shape functions into fn[1]...
    Load shape function derviatives into xDeriv[1]..., yDeriv[1]..., zDeriv[1]...
        For axisymmetric load zDeriv with shape function / particle radial position
        Input zDeriv must not be NULL
    Input: pointer to material point dimensionless position
   NOTE: This is called at various places in the time step when gradients are needed. It should
    recalculate the ones found at the beginning of the time step using the precalculated xipos
    or CPDI info, which are found in the initialization task
   throws CommonException() if too many CPDI nodes
*/
void ElementBase::GetShapeGradients(int *numnds,double *fn,int *nds,
                                    double *xDeriv,double *yDeriv,double *zDeriv,MPMBase *mpmptr) const
{
    Vector lp;
    
    switch(useGimp)
    {   case POINT_GIMP:
        	// load nodal numbers
            GetNodes(numnds,nds);
            
            // special case for regular mesh
            if(mpmgrid.GetCartesian()>0)
                ShapeFunction(mpmptr->GetNcpos(),TRUE,&fn[1],&xDeriv[1],&yDeriv[1],&zDeriv[1]);
            else
            {	// Load element coordinates
                Vector ce[MaxElNd];
                double fnh[MaxElNd];
                GetCoordinates(ce,*numnds,nds);
                
                // find shape functions and derviatives
                ShapeFunction(mpmptr->GetNcpos(),BMATRIX,&fn[1],&xDeriv[1],&yDeriv[1],&ce[1],NULL,NULL,&fnh[1]);
            }
            break;
            
        case UNIFORM_GIMP:
        {	// uGIMP analysis
            int ndIDs[maxShapeNodes];
            Vector *xipos = mpmptr->GetNcpos();
            mpmptr->GetDimensionlessSize(lp);
            GetGimpNodes(numnds,nds,ndIDs,xipos,lp);
            GimpShapeFunction(xipos,*numnds,ndIDs,TRUE,&fn[1],&xDeriv[1],&yDeriv[1],&zDeriv[1],lp);
            GimpCompact(numnds,nds,fn,xDeriv,yDeriv,zDeriv);
            break;
        }
            
        case UNIFORM_GIMP_AS:
        {	// uGIMP analysis
            int ndIDs[maxShapeNodes];
            Vector *xipos = mpmptr->GetNcpos();
            mpmptr->GetDimensionlessSize(lp);
            GetGimpNodes(numnds,nds,ndIDs,xipos,lp);
            GimpShapeFunctionAS(xipos,*numnds,ndIDs,TRUE,&fn[1],&xDeriv[1],&yDeriv[1],&zDeriv[1],lp);
            GimpCompact(numnds,nds,fn,xDeriv,yDeriv,zDeriv);
            break;
        }
            
        case LINEAR_CPDI:
		case LINEAR_CPDI_AS:
		case QUADRATIC_CPDI:
        {   if(theMaterials[mpmptr->MatID()]->Rigid())
			{	int ndIDs[maxShapeNodes];
                Vector *xipos = mpmptr->GetNcpos();
                mpmptr->GetDimensionlessSize(lp);
                GetGimpNodes(numnds,nds,ndIDs,xipos,lp);
                if(fmobj->IsAxisymmetric())
                    GimpShapeFunctionAS(xipos,*numnds,ndIDs,TRUE,&fn[1],&xDeriv[1],&yDeriv[1],&zDeriv[1],lp);
                else
					GimpShapeFunction(xipos,*numnds,ndIDs,TRUE,&fn[1],&xDeriv[1],&yDeriv[1],&zDeriv[1],lp);
				GimpCompact(numnds,nds,fn,xDeriv,yDeriv,zDeriv);
            }
            else
            {   *numnds = GetCPDIFunctions(nds,fn,xDeriv,yDeriv,zDeriv,mpmptr);
            }
            break;
		}
    }
}
/* Calculate Stiffness Matrix
*/
void CSTriangle::Stiffness(int np)
{
	double detjac,asr,dv;
	double xiDeriv[MaxElNd],etaDeriv[MaxElNd],asbe[MaxElNd],sfxn[MaxElNd];
	double bte[MxFree*MaxElNd][5],temp;
	double thck=thickness,deltaT;
	int numnds=NumberNodes();
	int ind1,ind2,i,j,irow,jcol,nst=2*numnds;
	MaterialBase *matl=theMaterials[material-1];
	Vector xi;
	
    // Load nodal coordinates (ce[]), temperature (te[]), and
	//    material props (pr.C[][] and pr.alpha[])
    GetProperties(np);
    
    // Zero upper hald element stiffness matrix (se[]) and reaction vector (re[])
    for(irow=1;irow<=nst;irow++)
    {   re[irow]=0.;
        for(jcol=irow;jcol<=nst;jcol++)
            se[irow][jcol]=0.;
    }

	/* Call shape routine to calculate element B (be,asbe) matrix
		and the determinant of the Jacobian - both at centriod */
	xi.x=(ce[1].x+ce[2].x+ce[3].x)/3.;
	xi.y=(ce[1].y+ce[2].y+ce[3].y)/3.;
	ShapeFunction(&xi,BMATRIX,&sfxn[1],&xiDeriv[1],&etaDeriv[1],&ce[1],
		&detjac,&asr,&asbe[1]);
	
	/* Form matrix product BT E - exploit known sparcity of
		B matrix and only include multiplications by nonzero elements */
	deltaT=0.;
	ind1=-1;
	if(np!=AXI_SYM)
	{	dv=thck*detjac;
		for(i=1;i<=numnds;i++)
		{	ind1=ind1+2;
			ind2=ind1+1;
			for(j=1;j<=3;j++)
			{	bte[ind1][j]=dv*(xiDeriv[i]*matl->pr.C[1][j]+etaDeriv[i]*matl->pr.C[3][j]);
				bte[ind2][j]=dv*(etaDeriv[i]*matl->pr.C[2][j]+xiDeriv[i]*matl->pr.C[3][j]);
			}
			deltaT+=te[i]*sfxn[i];
		}
	}
	else
	{	dv=asr*detjac;
		for(i=1;i<=numnds;i++)
		{	ind1=ind1+2;
			ind2=ind1+1;
			for(j=1;j<=4;j++)
			{	bte[ind1][j]=dv*(xiDeriv[i]*matl->pr.C[1][j]+etaDeriv[i]*matl->pr.C[3][j]
								+asbe[i]*matl->pr.C[4][j]);
				bte[ind2][j]=dv*(etaDeriv[i]*matl->pr.C[2][j]+xiDeriv[i]*matl->pr.C[3][j]);
			}
			deltaT+=te[i]*sfxn[i];
		}
	}

	/* Form stiffness matrix by getting BT E B and add in initial
		strains into element load vector */
	for(irow=1;irow<=nst;irow++)
	{	for(j=1;j<=3;j++)
		{	re[irow]+=bte[irow][j]*matl->pr.alpha[j]*deltaT;
		}
        
		if(np!=AXI_SYM)
		{	for(jcol=irow;jcol<=nst;jcol++)
			{	if(IsEven(jcol))
				{	ind1=jcol/2;
					temp=bte[irow][2]*etaDeriv[ind1]
							+bte[irow][3]*xiDeriv[ind1];
				}
				else
				{	ind1=(jcol+1)/2;
					temp=bte[irow][1]*xiDeriv[ind1]
							+bte[irow][3]*etaDeriv[ind1];
				}
				se[irow][jcol]+=temp;
			}
		}
		else
		{	re[irow]+=bte[irow][4]*matl->pr.alpha[j]*deltaT;
			for(jcol=irow;jcol<=nst;jcol++)
			{	if(IsEven(jcol))
				{	ind1=jcol/2;
					temp=bte[irow][2]*etaDeriv[ind1]
							+bte[irow][3]*xiDeriv[ind1];
				}
				else
				{	ind1=(jcol+1)/2;
					temp=+bte[irow][1]*xiDeriv[ind1]
							+bte[irow][3]*etaDeriv[ind1]
							+bte[irow][4]*asbe[ind1];
				}
				se[irow][jcol]+=temp;
			}
		}
	}

	/* Fill in lower half of stiffness matrix */
	for(irow=1;irow<=nst-1;irow++)
	{	for(jcol=irow+1;jcol<=nst;jcol++)
		{	se[jcol][irow]=se[irow][jcol];
		}
	}
}
/* Calculate Element forces, stresses, and strain energy
*/
void CSTriangle::ForceStress(double *rm,int np,int nfree)
{
	double sgp[5],etot[5];
	double temp,dv;
	double xiDeriv[MaxElNd],etaDeriv[MaxElNd],asbe[MaxElNd],sfxn[MaxElNd];
	double detjac,asr;
	double thck=thickness,deltaT;
	int numnds=3,nst=2*numnds;
	int i,j,ind1,ind2,ind,indg;
    MaterialBase *matl=theMaterials[material-1];
	Vector xi;
    
    // Load element coordinates (ce[]), noodal temperature (te[]), 
	//    and material props (pr.C[][] and pr.alpha[])
    GetProperties(np);
    
    // Load nodal displacements into re[]
    ind=0;
    for(j=1;j<=numnds;j++)
    {	indg=nfree*(nodes[j-1]-1);
        for(i=1;i<=nfree;i++)
            re[++ind]=rm[indg+i];
    }

    // zero force at each degree of freedom (stored in se[i][7])
    for(i=1;i<=nst;i++)
        se[i][7]=0.;
        
    // element strainEnergy
    strainEnergy=0.;

	// Zero stress and force vectors to hold results
	nst=2*numnds;
	for(i=1;i<=4;i++) sgp[i]=0.;

	/* Call shape routine to calculate element B (be,asbe) matrix
		and the determinant of the Jacobian - both at centriod */
	xi.x=(ce[1].x+ce[2].x+ce[3].x)/3.;
	xi.y=(ce[1].y+ce[2].y+ce[3].y)/3.;
	ShapeFunction(&xi,BMATRIX,&sfxn[1],&xiDeriv[1],&etaDeriv[1],&ce[1],
		&detjac,&asr,&asbe[1]);
			
	// Evaluate volume element
	if(np!=AXI_SYM)
        dv=thck*detjac;
	else
        dv=asr*detjac;

	/* Evaluate etot=(B d - e0). In forming
		B d, avoid multiplications by zero */
	deltaT=0.;
	for(i=1;i<=numnds;i++) deltaT+=te[i]*sfxn[i];
	etot[1]=-matl->pr.alpha[1]*deltaT;
	etot[2]=-matl->pr.alpha[2]*deltaT;
	etot[3]=-matl->pr.alpha[3]*deltaT;
	etot[4]=-matl->pr.alpha[4]*deltaT;
	ind1=-1;
	for(i=1;i<=numnds;i++)
	{	ind1=ind1+2;
		ind2=ind1+1;
		etot[1]+=xiDeriv[i]*re[ind1];
		etot[2]+=etaDeriv[i]*re[ind2];
		etot[3]+=etaDeriv[i]*re[ind1]+xiDeriv[i]*re[ind2];
		if(np==AXI_SYM)
			etot[4]+=asbe[i]*re[ind1];
	}

	// Multply by stiffness matrix: sig = mdm * etot
	if(np!=AXI_SYM)
	{	for(i=1;i<=3;i++)
		{	for(j=1;j<=3;j++)
				sgp[i]+=matl->pr.C[i][j]*etot[j];
		}
	}
	else
	{	for(i=1;i<=4;i++)
		{	for(j=1;j<=4;j++)
				sgp[i]+=matl->pr.C[i][j]*etot[j];
		}
	}

	// Add terms for getting integral(BT sig) which gives nodal forces
	ind1=-1;
	for(i=1;i<=numnds;i++)
	{	ind1=ind1+2;
		ind2=ind1+1;
		temp=xiDeriv[i]*sgp[1]+etaDeriv[i]*sgp[3];
		if(np==AXI_SYM)
			temp+=asbe[i]*sgp[4];
		se[ind1][7]+=temp*dv;
		temp=etaDeriv[i]*sgp[2]+xiDeriv[i]*sgp[3];
		se[ind2][7]+=temp*dv;
	}

	// Get initial/thermal strain contribution to strain energy
	temp=0.;
	for(i=1;i<=3;i++) temp+=sgp[i]*matl->pr.alpha[i]*deltaT;
	if(np==AXI_SYM) temp+=sgp[4]*matl->pr.alpha[4]*deltaT;
	strainEnergy-=0.5*temp*dv;

	/* When plane strain account for constrained 1D shrinkage effect
			on strain energy */
	if(np==PLANE_STRAIN)
		strainEnergy+=0.5*matl->pr.C[4][4]*deltaT*deltaT*dv;
	
	// Add 1/2 Fd to strain energy
	temp=0.;
	for(i=1;i<=nst;i++) temp+=re[i]*se[i][7];
	strainEnergy+=0.5*temp;

	// copy stress to all nodes
	for(i=1;i<=numnds;i++)
	{	for(j=1;j<=4;j++)
			se[i][j]=sgp[j];
	}

	/* For plane strain analysis, calculate sigz stress
		For axisymmetric, multiply force and energy by 2 pi */
	if(np==PLANE_STRAIN)
    {   for(i=1;i<=numnds;i++)
			se[i][4]=matl->GetStressStrainZZ(se[i][1],se[i][2],se[i][3],te[i],angle,np);
    }
	else if(np==AXI_SYM)
	{	for(i=1;i<=nst;i++)
			se[i][7]*=2.*PI_CONSTANT;
		strainEnergy*=2*PI_CONSTANT;
	}
}