/* set next point and find orientation
    return ANGLED (-1) if line not along x or y axis
*/
int ContourPoint::SetNextPoint(ContourPoint *apt)
{
    double dl;
    
    nextPoint=apt;
    double dx=nextPoint->node->x-node->x;
    double dy=nextPoint->node->y-node->y;
    if(DbleEqual(dx,0.))
    {	orient=VERTICAL;
        norm.x=dy>0 ? 1. : -1.;
        norm.y=0;
        ds=dy/norm.x;
    }
    else if(DbleEqual(dy,0.))
    {	orient=HORIZONTAL;
        norm.x=0.;
        norm.y=dx>0 ? -1. : 1.;
        ds=-dx/norm.y;
    }
    else
    {	orient=ANGLED;
        dl=sqrt(dx*dx+dy*dy);
        norm.x=dy/dl;
        norm.y=-dx/dl;
        ds=dl;			// redo if use off-axis contours
    }
    return orient;
}
// Convert J to K assuming an isotropic material
// d -- crack opening displacement near crack tip in mm, d.y--opening, d.x--shear
// C -- crack propagating velocity in mm/sec
// J0 -- J integral components in J0.x and J0.y in uN/mm
// np -- PLANE_STRESS_MPM or PLANE_STRAIN_MPM (axisymmetry not certain, current reverts to plane strain)
// nuLS and GLS -- low strain Poisson's ratio and shear modulus (in Pa = uN/mm^2)
Vector MaterialBase::IsotropicJToK(Vector d,Vector C,Vector J0,int np,double nuLS,double GLS)
{
    double Cs2,Cd2,C2;
    double B1,B2,A1,A2,A3,A4,DC;
    double term1,term2;
    Vector SIF;
	
    double dx = d.x;
    double dy = d.y;
    double J0x = fabs(J0.x);                        // J0.x should be always positive
	
	double kf=0.;
    if(np==PLANE_STRESS_MPM)
        kf=(3.-nuLS)/(1.+nuLS);
	else
        kf=3.-4.*nuLS;
	
    C2 = C.x*C.x+C.y*C.y;				// square of crack velocity
	// dynamic or stationary crack
    if(!DbleEqual(sqrt(C2),0.0)) 
	{	Cs2=GLS/rho;
        Cd2=Cs2*(kf+1.)/(kf-1.);
        B1=sqrt(1.-C2/Cd2);
        B2=sqrt(1.-C2/Cs2);
        DC=4*B1*B2-(1.+B2*B2)*(1.+B2*B2);
        A1=B1*(1.-B2*B2)/DC;
        A2=B2*(1.-B2*B2)/DC;
        A3=1./B2;
        term1=0.5*(4.*B1*B2+(1.+B2*B2)*(1.+B2*B2))*(2.+B1+B2)/sqrt((1.+B1)*(1.+B2));
        A4=(B1-B2)*(1.-B2*B2)*(term1-2.*(1.+B2*B2))/DC/DC;
    }
    else
	{	B1=B2=1.0;
        A3=1.;
        A1=A2=A4=(kf+1.)/4.;
    }
	
    term2=dy*dy*B2+dx*dx*B1;			// mm^2
	
	// special case for zero COD
    if(DbleEqual(term2,0.0))
	{	SIF.x = 0.0;
		SIF.y = 0.0;
    }
    else
	{	// Units mm sqrt(uN/mm^2 uN/mm 1/mm^2) = uN/mm^2 sqrt(mm)
		SIF.x = dy*sqrt(2*GLS*J0x*B2/A1/term2);
		SIF.y = dx*sqrt(2*GLS*J0x*B1/A2/term2);
    }
    
    return SIF;
}
Example #3
0
//	Find extent of this element - called once at start (and must be called)
void Lagrange2D::FindExtent(void)
{	
	ElementBase::FindExtent();
	
	/* for speed in GetXiPos() calculations - if it a parallelogram
	 Note: assumes element does not move. If it does, must recalculate these terms
	 */
	pgElement=TRUE;
	// are edges parallel wrt x coordinate?
	double xdel=fabs(nd[nodes[2]]->x-nd[nodes[1]]->x);
    double ydel=fabs(nd[nodes[3]]->x-nd[nodes[0]]->x);
	if(!DbleEqual(xdel,ydel) && xdel>1.e-10 && ydel>1.e-10)
	{	pgElement=FALSE;
		return;
	}

	// are edges parallel wrt y coordinate?
	xdel=fabs(nd[nodes[2]]->y-nd[nodes[1]]->y);
	ydel=fabs(nd[nodes[3]]->y-nd[nodes[0]]->y);
	if(!DbleEqual(xdel,ydel) && xdel>1.e-10 && ydel>1.e-10)
	{	pgElement=FALSE;
		return;
	}

	// mid side nodes in line?
	int i;
	for(i=0;i<3;i++)
	{	if(!DbleEqual(2.*nd[nodes[i+4]]->x, nd[nodes[i]]->x+nd[nodes[i+1]]->x) ||
				!DbleEqual(2.*nd[nodes[i+4]]->y, nd[nodes[i]]->y+nd[nodes[i+1]]->y))
		{	pgElement=FALSE;
			return;
		}
	}
	if(!DbleEqual(2.*nd[nodes[7]]->x, nd[nodes[3]]->x+nd[nodes[0]]->x) ||
		   !DbleEqual(2.*nd[nodes[7]]->y, nd[nodes[3]]->y+nd[nodes[0]]->y))
	{	pgElement=FALSE;
		return;
	}
		
	// Is center node in the center?
	if(!DbleEqual(2.*nd[nodes[8]]->x, nd[nodes[4]]->x+nd[nodes[6]]->x) ||
	   !DbleEqual(2.*nd[nodes[8]]->y, nd[nodes[4]]->y+nd[nodes[6]]->y))
	{	pgElement=FALSE;
		return;
	}
	
	
	// precalculate useful terms
	pgTerm[0]=nd[nodes[0]]->x+nd[nodes[1]]->x+nd[nodes[2]]->x+nd[nodes[3]]->x;
	pgTerm[1]=nd[nodes[1]]->x+nd[nodes[2]]->x-nd[nodes[0]]->x-nd[nodes[3]]->x;
	pgTerm[2]=nd[nodes[2]]->x+nd[nodes[3]]->x-nd[nodes[0]]->x-nd[nodes[1]]->x;
	pgTerm[3]=nd[nodes[0]]->y+nd[nodes[1]]->y+nd[nodes[2]]->y+nd[nodes[3]]->y;
	pgTerm[4]=nd[nodes[1]]->y+nd[nodes[2]]->y-nd[nodes[0]]->y-nd[nodes[3]]->y;
	pgTerm[5]=nd[nodes[2]]->y+nd[nodes[3]]->y-nd[nodes[0]]->y-nd[nodes[1]]->y;
	double det=pgTerm[1]*pgTerm[5]-pgTerm[2]*pgTerm[4];
	pgTerm[1]/=det;
	pgTerm[2]/=det;
	pgTerm[4]/=det;
	pgTerm[5]/=det;
}
// Print contact law settings and finalize variables
void CrackSurfaceContact::CrackOutput(bool custom,double customFriction,double customDn,double customDnc,
									  double customDt,int number)
{
	if(!custom || (customFriction<10. && DbleEqual(friction,customFriction)) || 
	   (friction>10. && customFriction>10. && DbleEqual(Dn,customDn) && DbleEqual(Dt,customDt) &&  DbleEqual(Dnc,customDnc)))
	{	CrackContactLaw[number].law=ContactLaw;
		CrackContactLaw[number].friction=friction;
		CrackContactLaw[number].Dn=Dn;
		CrackContactLaw[number].Dnc=Dnc;
		CrackContactLaw[number].Dt=Dt;
		return;
	}
	
	char hline[200];
	
	CrackContactLaw[number].friction=customFriction;
	CrackContactLaw[number].Dn=customDn;
	CrackContactLaw[number].Dnc=customDnc;
	CrackContactLaw[number].Dt=customDt;
	if(customFriction<-10.)
	{   CrackContactLaw[number].law=NOCONTACT;
		sprintf(hline,"contacts ignored");
	}
	else if(customFriction<-.5)
	{   CrackContactLaw[number].law=STICK;
		sprintf(hline,"stick conditions");
	}
	else if(customFriction>10.)
	{   CrackContactLaw[number].law=IMPERFECT_INTERFACE;
		if(customDnc<-100.e6) customDnc=customDn;
		const char *label = UnitsController::Label(INTERFACEPARAM_UNITS);
		sprintf(hline,"imperfect interface: Dn = %g %s, Dnc = %g %s, Dt = %g %s",
				customDn*UnitsController::Scaling(1.e-6),label,
				customDnc*UnitsController::Scaling(1.e-6),label,
				customDt*UnitsController::Scaling(1.e-6),label);
		hasImperfectInterface=TRUE;
	}
	else if(customFriction>0.)
	{   CrackContactLaw[number].law=FRICTIONAL;
		sprintf(hline,"frictional with coefficient of friction: %.6f",customFriction);
	}
	else
	{   CrackContactLaw[number].law=FRICTIONLESS;
		CrackContactLaw[number].friction=0.;		// to be sure
		sprintf(hline,"frictionless sliding");
	}
	cout << "    Custom Contact: " << hline << endl;
}
// Calculate properties used in analyses - here trilinear law
const char *TrilinearTraction::VerifyAndLoadProperties(int np)
{
	const char *msg=SetTLTractionLaw(stress1,kI1,umidI,sI2,uI2,delIc,JIc);
	if(msg!=NULL) return msg;
	
	msg=SetTLTractionLaw(stress2,kII1,umidII,sII2,uII2,delIIc,JIIc);
	if(msg!=NULL) return msg;
	
	const char *err = TractionLaw::VerifyAndLoadProperties(np);
	
	// See if break points are the same
	break1is2I = (bool)DbleEqual(umidI,uI2);
	break1is2II = (bool)DbleEqual(umidII,uII2);
	
	return err;
}
Example #6
0
// Get derivative of sqrt(2./3)*yield wrt ln(epdot), but if constantYT,
//  get derivative of sqrt(2./3.)*yield wrt lambda or (2./3)*yield wrt alpha for plane strain and 3D
// ... and using dep/dlambda = sqrt(2./3.)
// ... and epdot = dalpha/delTime with dalpha = sqrt(2./3.)lamda or depdot/dlambda = sqrt(2./3.)/delTime
double SLMaterial::GetKPrime(MPMBase *mptr,int np,double delTime,HardeningAlpha *a,void *properties) const
{
	SLProperties *p = (SLProperties *)properties;
	
	// aThermal terms
	
	// slope zero if in constant max yield condition
	double aThermal=0.;
	if(yldred*pow(1.+beta*a->alpint,nhard)<yldMaxred)
	{	aThermal = DbleEqual(nhard,1.) ? yldred*beta :
				yldred*beta*nhard*pow(1.+beta*a->alpint,nhard-1.) ;
	}
	
	// if constantYT, then not rate dependent terms and want derivative wrt lambda
	if(p->isConstantYT)
	{	return TWOTHIRDS*aThermal*p->Gratio;
	}
	
	// Get derivative of sqrt(2./3.)*yield with respect to epdot times epdot (i.e., wrt ln(epdot)) for plane strain and 3D
	double YTslope = 0.;
	double epdot = a->dalpha/delTime;
	if(epdot>0. && epdot<=p->epdotmax)
	{	// Find slope from current values
		double YTred = p->currentYTred;				// recall current
		double arg = C2red/YTred;
		double slope = epdot*arg/YTred + (1. - epdot*arg)*2.*p->TwoUkkT*(1.-YTred/YPred)/YPred;
		YTslope = 1./slope;
	}
	
	// combine and return derivative wrt ln(epdot)
	return SQRT_TWOTHIRDS*(aThermal*a->dalpha + YTslope)*p->Gratio;
}
Example #7
0
// Do these edges intersect?
// Inspired by paper: "A Vertex Program for Efficient Box-Plane Intersection"
// by Christof Rezk Salama and Andreas Kolb, 2005
bool IntersectEdges(int i,int j,Vector *Point,Vector *n)
{
// Vertices of edge ij
    Vector Vertex_i = MakeCubeCorner(i);  // vertex i
    Vector Edge_ij = MakeCubeCorner(j);   // vertex j
    Edge_ij.x -=Vertex_i.x;    // difference of coordinates
    Edge_ij.y -=Vertex_i.y;
    Edge_ij.z -=Vertex_i.z;
// Start calculating lambda
    double lambda = -DotVectors(&Vertex_i,n);  // numerator of lambda
    double denom = DotVectors(&Edge_ij,n); // denominator of lambda

// Don't divide by zero
    if(DbleEqual(denom,0.0))
    {
        return false;  // colinear
    }

// Calculate lambda
    lambda = lambda/denom;
// if not in [0,1] then it doesn't intersect
    if(lambda>1.0 || lambda<0.0) {
        return false;  // doesn't intersect
    }
// Find point
    Point->x = Vertex_i.x +lambda*Edge_ij.x;
    Point->y = Vertex_i.y +lambda*Edge_ij.y;
    Point->z = Vertex_i.z +lambda*Edge_ij.z;
    return true; // it does intersect
}
// calculate properties for give state
const char *BistableIsotropic::CurrentProperties(short newState,int np)
{
    double K;
	const char *err=NULL;
    
    // find properties for state
    if(newState==INITIAL_STATE)
    {	K=K0;
        G=G0;
        aI=a0;
		betaI=beta0;
		diffusionCon=diff0;
		kCond=kCond0;
    }
    else
    {	K=Kd;
        G=Gd;
        aI=ad;
		betaI=betad;
		diffusionCon=diffd;
		kCond=kCondd;
    }
    
    // analysis properties this state
    E=9.*K*G/(G + 3.*K);
    nu=(3.*K-2.*G)/(6.*K+2.*G);
	if(DbleEqual(E,0.0))
		return "State with zero modulus is not supported.";
    err=SetAnalysisProps(np,E,E,E,nu,nu,nu,G,G,G,
			1.e-6*aI,1.e-6*aI,1.e-6*aI,betaI*concSaturation,betaI*concSaturation,betaI*concSaturation);
	
	return err;
}
Example #9
0
// Print contact law settings for cracks and finalize variables
void CrackSurfaceContact::Output(int numberOfCracks)
{
	char hline[200];
	
	// Default contact law
	if(friction<-10.)
	{   ContactLaw=NOCONTACT;
		sprintf(hline,"contacts ignored");
	}
	else if(friction<-.5)
	{   ContactLaw=STICK;
		sprintf(hline,"stick conditions");
	}
	else if(DbleEqual(friction,(double)0.))
	{   ContactLaw=FRICTIONLESS;
		sprintf(hline,"frictionless sliding");
	}
	else if(friction>10.)
	{   ContactLaw=IMPERFECT_INTERFACE;
		if(Dnc<-100.) Dnc=Dn;
		sprintf(hline,"imperfect interface\n     Dnt = %g MPa/mm, Dnc = %g MPa/mm, Dt = %g MPa/mm",
				Dn,Dnc,Dt);
		hasImperfectInterface=TRUE;
	}
	else
	{   ContactLaw=FRICTIONAL;
		sprintf(hline,"frictional with coefficient of friction: %.6f",friction);
	}
	
	// print results
	cout << "Default Contact: " << hline << endl;
#ifdef _VELOCITY_ONLY_
	cout << "Contact Detection: Normal dv < 0" << endl;
#else
	cout << "Contact Detection: Normal cod < 0 AND normal dv < 0" << endl;
    mpmgrid.OutputContactByDisplacements();
#endif
	if(GetMoveOnlySurfaces())
		cout << "Crack Plane Updating: Average of the crack surfaces" << endl;
	else
		cout << "Crack Plane Updating: Use center of mass velocity" << endl;
	if(GetPreventPlaneCrosses())
		cout << "Crack Plane Crosses: surface particles moved back to the current plane" << endl;
	else
		cout << "Crack Plane Crosses: ignored" << endl;
    
	// allocate memory for custom crack contact laws
	char *p=new char[(numberOfCracks+1)*sizeof(ContactDetails)];
    
    // this is the default contact law
	CrackContactLaw=(ContactDetails *)p;
	CrackContactLaw[0].law=ContactLaw;
	CrackContactLaw[0].friction=friction;
	CrackContactLaw[0].Dn=Dn;
	CrackContactLaw[0].Dnc=Dnc;
	CrackContactLaw[0].Dt=Dt;
}
Example #10
0
// Get derivative of sqrt(2./3.)*yield with respect to lambda for plane strain and 3D
// ... and using dep/dlambda = sqrt(2./3.)
// ... and epdot = dalpha/delTime with dalpha = sqrt(2./3.)lambda or depdot/dlambda = sqrt(2./3.)/delTime
double DDBHardening::GetKPrime(MPMBase *mptr,int np,double delTime,HardeningAlpha *a,void *properties) const
{
	DDBHProperties *p = (DDBHProperties *)properties;
	if(!DbleEqual(a->dalpha,0.))
		//return (p->yieldC-p->yieldP)/(a->dalpha*parent->rho); 
		return (p->yieldC-p->yieldP)/a->dalpha; // Removed the rho as already reduced in GetYield.
	else 
		return 0;
		
}
Example #11
0
// Return yield stress for current conditions (alpint for cum. plastic strain and dalpha/delTime for plastic strain rate)
double DDBHardening::GetYield(MPMBase *mptr,int np,double delTime,HardeningAlpha *a,void *properties) const
{
	DDBHProperties *p = (DDBHProperties *)properties;

	p->rhoCtemp = p->rhoC;
	p->rhoWtemp = p->rhoW;

	// update rhoc/w from previous increment
	p->rhoWtemp += p->rhoWDot*delTime;
	p->rhoCtemp += p->rhoCDot*delTime;
		
	// update values for strain and strain rate
	double eqss = SQRT_THREE*a->alpint;
	double rss = tayM*eqss;
	double eqssra = SQRT_THREE*(a->dalpha/delTime);
	double rssra = tayM*eqssra;

	// update value of volume fraction
	p->fr = fLim + (fo-fLim)*exp(-1.*rss/fsto);
		
	p->rhoT = p->fr*p->rhoWtemp+(1.-p->fr)*p->rhoCtemp;
	p->dSize = K1/sqrt(p->rhoT);
		
		// update dislocation density evolution rate in cell wall and cell interior
		if(!DbleEqual(rssra,0.))
		{	double wAdd = (6.*esbe*rssra*pow(1-p->fr,TWOTHIRDS))/(burg*p->dSize*p->fr);
			double wRem = (SQRT_THREE*esbe*rssra*(1.-p->fr)*sqrt(p->rhoWtemp))/(p->fr*burg);
			double wDis = -disk1*pow(rssra/sto,-1./p->N)*rssra*p->rhoWtemp;
			p->rhoWDotTemp = wAdd+wRem+wDis;
			
			double cAdd = esal*SQRT_ONETHIRD*(sqrt(p->rhoWtemp)/burg)*rssra;
			double cRem = -esbe*((6.*rssra)/(burg*p->dSize*pow(1.-p->fr,ONETHIRD)));
			double cDis = -disk1*pow(rssra/sto,-1./p->N)*rssra*p->rhoCtemp;
			p->rhoCDotTemp = cAdd+cRem+cDis;
		}
		
	// update 
	double sigoc=alp*MMG*burg*sqrt(p->rhoCtemp);
	double rstc=sigoc*pow(rssra/sto,1./p->SHM);
	
	// update 
	double sigow=alp*MMG*burg*sqrt(p->rhoWtemp);
	double rstw=sigow*pow(rssra/sto,1./p->SHM);
	
	// update stress
	double rst=p->fr*rstw+(1.-p->fr)*rstc;
	p->yieldP = p->yieldC;       
	p->yieldInc = tayM*rst*(SQRT_THREE/parent->rho);
	p->yieldC = p->yieldInc + yldred;
	
	// return new stress value
	return p->yieldC;
		
}
Example #12
0
// these are a triangle face ccw when looking from outside the polyhedron
PolyTriangle::PolyTriangle(Vector vp0,Vector vp1,Vector vp2)
{
	v0=vp0;
	v1=vp1;
	v2=vp2;
	
	// normal is (v1-v0) X (v2-v0)
	Vector a,b;
	SubVector(CopyVector(&a,&v1),&v0);
	SubVector(CopyVector(&b,&v2),&v0);
	CrossProduct(&n,&a,&b);
	
	if(!DbleEqual(n.x,0))
	{	m11 = b.z/n.x;
		m12 = -b.y/n.x;
		m21 = -a.z/n.x;
		m22 = a.y/n.x;
		style = USE_NX;
	}
	else if(!DbleEqual(n.z,0))
	{	m11 = b.y/n.z;
		m12 = -b.x/n.z;
		m21 = -a.y/n.z;
		m22 = a.x/n.z;
		style = USE_NZ;
	}
	else
	{	m11 = -b.z/n.y;
		m12 = b.x/n.y;
		m21 = a.z/n.y;
		m22 = -a.x/n.y;
		style = USE_NY;
	}
	
	fmin.x = fmin(fmin(v0.x,v1.x),v2.x);
	fmin.y = fmin(fmin(v0.y,v1.y),v2.y);
	fmin.z = fmin(fmin(v0.z,v1.z),v2.z);
	fmax.x = fmax(fmax(v0.x,v1.x),v2.x);
	fmax.y = fmax(fmax(v0.y,v1.y),v2.y);
	fmax.z = fmax(fmax(v0.z,v1.z),v2.z);
}
Example #13
0
// Print contact law settings for cracks and finalize variables
void MaterialBase::ContactOutput(int thisMatID)
{
	char hline[200];
	
	ContactDetails *currentFriction=lastFriction;
	
	if(currentFriction!=NULL)
		cout << "Custom contact between material " << thisMatID << " and" << endl;
	
    // Law determined by friction coefficient
    // <= -10 : Revert to single velocity field
    // -1 to -9 (actually -10 < friction < -.5 : stick conditions in contact, free in separation
    // 0 : frictionless
    // >=10 : imperfect interface
    // otherwise : friction with that coefficient of friction
	while(currentFriction!=NULL)
	{	// Custom Contact
		if(currentFriction->friction<=-10.)
		{   currentFriction->law=NOCONTACT;
			sprintf(hline,"contact nodes revert to center of mass velocity field");
		}
		else if(currentFriction->friction<-.5)
		{   currentFriction->law=STICK;
			sprintf(hline,"stick conditions");
		}
		else if(DbleEqual(currentFriction->friction,(double)0.))
		{   currentFriction->law=FRICTIONLESS;
			sprintf(hline,"frictionless sliding");
		}
		else if(currentFriction->friction>10.)
		{   currentFriction->law=IMPERFECT_INTERFACE;
			if(currentFriction->Dnc<-100.e6) currentFriction->Dnc=currentFriction->Dn;
			const char *label = UnitsController::Label(INTERFACEPARAM_UNITS);
			sprintf(hline,"imperfect interface\n         Dn = %g %s, Dnc = %g %s, Dt = %g %s",
					currentFriction->Dn*UnitsController::Scaling(1.e-6),label,
					currentFriction->Dnc*UnitsController::Scaling(1.e-6),label,
					currentFriction->Dt*UnitsController::Scaling(1.e-6),label);
		}
		else
		{   currentFriction->law=FRICTIONAL;
			sprintf(hline,"frictional with coefficient of friction: %.6f",currentFriction->friction);
		}
		
		cout << "     material " << currentFriction->matID << ": " << hline << endl;
		
		currentFriction=(ContactDetails *)currentFriction->nextFriction;
	}
}
Example #14
0
// Verify input properties do calculations; if problem return string with an error message
// Don't pass on to material base
const char *CoulombFriction::VerifyAndLoadProperties(int np)
{
	if(frictionCoeff<0.)
	{	frictionStyle = STICK;
		frictionCoeffStatic = -1.;
	}
	
	else if(DbleEqual(frictionCoeff,(double)0.))
		frictionStyle = FRICTIONLESS;
	
	else
		frictionStyle = FRICTIONAL;
	
	if(frictionCoeffStatic>=0. && frictionCoeffStatic<frictionCoeff)
		return "The static coefficient of friction cannot be lower than the sliding coefficient of friction";
	
	// must call super class
	return ContactLaw::VerifyAndLoadProperties(np);
}
Example #15
0
/* find hoop direction unit vector relative to crack direction. 
	If theta is the ccw rotation of hoop direction relative to crack tip direction, then
		hooopDir = (cos(theta), sin(theta))
	To get vector in hoop direction later use;
		(crackDir.x*hoopDir.x - crackDir.y*hoopDir.y, crackDir.x*hoopDir.y + crackDir.y*hoopDir.x)
*/
void MaterialBase::HoopDirection(double KI,double KII,Vector *hoopDir)
{
	// pure mode II (70.5 degrees)
	if(DbleEqual(KI,0.0))
	{	hoopDir->x=1./3.;
		hoopDir->y=sqrt(8./9.);
		if(KII>=0.) hoopDir->y=-hoopDir->y;
	}
	
	// pure mode I or mixed mode
	else
	{	double KI2=KI*KI;
		double KII2=KII*KII;
		
		// these really obey hoopDir->x^2 + hoopDir->y^2 = 1
		hoopDir->x=(3*KII2 + KI*sqrt(KI2+8.*KII2))/(KI2+9*KII2);
		hoopDir->y=fabs(KII*(3*hoopDir->x-1.)/KI);
		if(KII>=0.) hoopDir->y=-hoopDir->y;
	}
}
Example #16
0
// Get derivative of (1./3.)*yield^2 with respect to lambda for plane stress only
// ... and using dep/dlambda = sqrt(2./3.)*fnp1 where ep=alpint
// ... and epdot = dalpha/delTime with dalpha = sqrt(2./3.)*lambda*fnp1 or depdot/dlambda = sqrt(2./3.)*fnp1/delTime
// Also equal to sqrt(2./3.)*GetYield()*GetKPrime()*fnp1, but in separate call for efficiency
double Nonlinear2Hardening::GetK2Prime(MPMBase *mptr,double fnp1,double delTime,HardeningAlpha *a,void *properties) const
{
    if(DbleEqual(a->alpint,0.)) return 0.;
    double alphan = pow(a->alpint,npow);
	return SQRT_EIGHT27THS*yldred*yldred*beta*npow*(1.+beta*alphan)*alphan*fnp1/a->alpint;
}
Example #17
0
// Crack Propagation Criterion
//	Input is crack tip segment (which will have needed parameters)
//		and crackDir is direction at crack tip (unit vector)
//	Output is
//		NOGROWTH - nothing more to do
//		GROWNOW - propagate and see if speed needs control
//	If GROWNOW, change crackDir to unit
//		vector in crack growth direction
//  If need J or K, must say so in CriterionNeeds() routine
int MaterialBase::ShouldPropagate(CrackSegment *crkTip,Vector &crackDir,CrackHeader *theCrack,int np,int critIndex)
{	
    double KI,KII,fCriterion,cosTheta0,sinTheta0,cosTheta2;
	Vector hoopDir;

    // retrieve fracture parameters
    switch(criterion[critIndex])
	{	// Criterion 1
        case MAXHOOPSTRESS:
            // Maximum hoop stress (or maximum principal stress) criterion only applies to
            // isotropic elastic materials
            KI=crkTip->sif.x;
            KII=crkTip->sif.y;
			HoopDirection(KI,KII,&hoopDir);
            
            // failure criterion
            cosTheta2=sqrt((1.+hoopDir.x)/2.);
            fCriterion=KI*pow(cosTheta2,3)-1.5*KII*cosTheta2*hoopDir.y-KIc;
        
            // growth, return direction (unless overridden)
            if(fCriterion>0.)
			{	if(!SelectDirection(crkTip,crackDir,theCrack,critIndex))
					RotateDirection(&crackDir,hoopDir.x,hoopDir.y);
                return GROWNOW;
            }
            break;
        
		// Criterion 7
		case CRITICALERR:
            // growth, direction by direction option
            if(crkTip->Jint.x>=JIc)
			{	SelectDirection(crkTip,crackDir,theCrack,critIndex);
				return GROWNOW;
            }
			break;
			
		// Criterion 2
        case STEADYSTATEGROWTH:
            switch(crkTip->steadyState)
            {	case STATIONARY:
                    // If J now > JIc start propagating at constant speed
					//   or if time was specified, go on that time
					if(initTime<0.)
                    {	if(crkTip->Jint.x<JIc) return NOGROWTH;
					}
					else
					{	if(mtime<initTime) return NOGROWTH;
					}
                    crkTip->steadyState=PROPAGATING;
                    crkTip->speed=initSpeed;		// constant speed
                    break;
                
                case PROPAGATING:
                    // continue propagating unless reached maximum length
                    if(maxLength>0)
                    {	if(theCrack->Length()>=maxLength)
                        {   crkTip->steadyState=ARRESTING;
                            return NOGROWTH;
                        }
                    }
                    break;
                
                case ARRESTING:
                    // ARRESTING applies to first interval after propagation stops
                    crkTip->steadyState=ARRESTED;
                case ARRESTED:
                    return NOGROWTH;
                
                default:
                    break;
            }
            
            // if not overriden self similar or a constant direction
			if(!SelectDirection(crkTip,crackDir,theCrack,critIndex))
			{	if(constantDirection) crackDir=growDir;		// if direction specified in material
			}
            return GROWNOW;
            break;
        
		// Criterion 3 no longer used
		// Criterion 4
        case STRAINENERGYDENSITY:
            // Minimum strain energy density criterion only applies to 
            // isotropic elastic materials
            KI=crkTip->sif.x;
            KII=crkTip->sif.y;

            // Poisson's ratio and the constant k
            double v,k;
            v=C12/C11;
            k=(np==PLANE_STRESS_MPM)? (3-v)/(1+v) : (3-4*v);

            // Crack propagation direction
            // pure mode I
            if(DbleEqual(KII,0.0))
            {   cosTheta0 = 1.0;
                sinTheta0 = 0.0;
            }

            // pure mode II or mixed mode
            // crack propagation direction by solving the criterion equation numerically.
            else
            {   double theta0=CrackPropagationAngleFromStrainEnergyDensityCriterion(k,KI,KII); 
                cosTheta0=cos(theta0);
                sinTheta0=sin(theta0);
            }

            // failure criterion of strain energy density
            double a11,a12,a22;
            a11=(1+cosTheta0)*(k-cosTheta0);
            a12=sinTheta0*(2*cosTheta0-k+1);
            a22=(k+1)*(1-cosTheta0)+(1+cosTheta0)*(3*cosTheta0-1);
            fCriterion=sqrt((a11*KI*KI+2*a12*KI*KII+a22*KII*KII)/2/(k-1))-KIc;

            // growth, return direction
            if(fCriterion>0.)
			{	if(!SelectDirection(crkTip,crackDir,theCrack,critIndex))
					RotateDirection(&crackDir,cosTheta0,sinTheta0);
                return GROWNOW;
            }
            break;

		// Criterion 5
        case EMPIRICALCRITERION:
            // Empirical criterion (KI/KIc)^p+(KII/KIIc)^q=1 only applies to
            // isotropic elastic materials.
            KI=crkTip->sif.x;
            KII=crkTip->sif.y;

            // Crack propagation direction
            // pure mode I
            if(DbleEqual(KII,0.0))
            {   cosTheta0 = 1.0;
                sinTheta0 = 0.0;
            }               
                            
            // For pure mode II or mixed mode, detremine crack propagation direction
            // by maximum principal stress criterion since the empirical criterion can only 
            // describe fracture locus. 
            else        
            {   double R=KI/KII;
                int sign = (KII>=0.)? -1 : 1;
                double theta0=2*atan((R+sign*sqrt(R*R+8))/4.);
                cosTheta0=cos(theta0);
                sinTheta0=sin(theta0);
            }       

            // failure criterion of strain energy density
            fCriterion=(pow(KI/KIc,KIexp)+pow(KII/KIIc,KIIexp))*KIc-KIc;
                    
            // growth, return direction
            if(fCriterion>0.)
			{	if(!SelectDirection(crkTip,crackDir,theCrack,critIndex))
					RotateDirection(&crackDir,cosTheta0,sinTheta0);
                return GROWNOW;         
            }
            break;
		
		// criterion 6 - fail if either CTOD exists, default by self simlar
		//	some critical value
		case MAXCTODCRITERION:
			double codn,cods;
			Vector cod;
			theCrack->GetCOD(crkTip,cod,true);
			cods=fabs(cod.x);
			codn=fabs(cod.y);
			if(cods>delIIc && delIIc>0.)
			{	SelectDirection(crkTip,crackDir,theCrack,critIndex);
				return GROWNOW;
			}
			if(codn>delIc && delIc>0.)
			{	SelectDirection(crkTip,crackDir,theCrack,critIndex);
				return GROWNOW;
			}
			break;

        default:
            break;
    }
    
    return NOGROWTH;
}
// Print contact law settings for cracks and finalize variables
void CrackSurfaceContact::MaterialOutput(void)
{
	char hline[200];
	
	// Global material contact
	if(materialFriction<-10.)
	{   materialContactLaw=NOCONTACT;
		sprintf(hline,"contact nodes revert to center of mass velocity field");
	}
	else if(materialFriction<-.5)
	{   materialContactLaw=STICK;
		sprintf(hline,"stick conditions");
	}
	else if(DbleEqual(materialFriction,(double)0.))
	{   materialContactLaw=FRICTIONLESS;
		sprintf(hline,"frictionless sliding");
	}
	else if(materialFriction>10.)
	{   materialContactLaw=IMPERFECT_INTERFACE;
		if(materialDnc<-100.e6) materialDnc=materialDn;
		const char *label = UnitsController::Label(INTERFACEPARAM_UNITS);
		sprintf(hline,"imperfect interface\n     Dnt = %g %s, Dnc = %g %s, Dt = %g %s",
				materialDn*UnitsController::Scaling(1.e-6),label,
				materialDnc*UnitsController::Scaling(1.e-6),label,
				materialDt*UnitsController::Scaling(1.e-6),label);
	}
	else
	{   materialContactLaw=FRICTIONAL;
		sprintf(hline,"frictional with coefficient of friction: %.6f",materialFriction);
	}
	
	// print results
	char join[3];
	join[0]=0;
	cout << "Default Contact: " << hline << endl;
	cout << "Contact Detection: ";
	if(materialContactVmin>0.)
	{	cout << "(Vrel >= " << materialContactVmin << ")";
		strcpy(join," & ");
	}
	cout << join << "(Normal dv < 0)";
	strcpy(join," & ");
	if(displacementCheck)
	{	cout << join << "(Normal cod < 0)" << endl;
        mpmgrid.OutputContactByDisplacements();
	}
	else
		cout << endl;
	
	cout << "Normal Calculation: ";
	switch(materialNormalMethod)
	{	case MAXIMUM_VOLUME_GRADIENT:
			cout << " gradient of material or paired material (if all nonrigid), or the rigid material (if" << endl;
			cout << "                   one rigid material), that has highest magnitude. When has rigid" << endl;
			cout << "                   material, prefer rigid material gradient with bias factor = " << rigidGradientBias;
			rigidGradientBias*=rigidGradientBias;       // squared as needed in algorithm
			break;
		case MAXIMUM_VOLUME:
			cout << " gradient of material with maximum volume";
			break;
		case AVERAGE_MAT_VOLUME_GRADIENTS:
			cout << " volume-weighted mean gradient of material and other materials lumped (if all nonrigid)," << endl;
			cout << "                   on just the rigid material (if one rigid material). When has rigid" << endl;
			cout << "                   material, prefer rigid material gradient with bias factor = " << rigidGradientBias;
			rigidGradientBias*=rigidGradientBias;       // squared as needed in algorithm
			break;
		case EACH_MATERIALS_MASS_GRADIENT:
			cout << " each material's own mass gradient";
			break;
		case SPECIFIED_NORMAL:
			cout << " use the specified normal of ";
			PrintVector("",&contactNormal);
			cout << endl;
		default:
			break;
	}
	cout << endl;
    
    // development flags for multimaterial contact
    if(fmobj->dflag[0] > 0)
    {   cout << "** Development flag for custom contact **" << endl;
        switch(fmobj->dflag[0])
		{	case 4:
                cout << "   Special normals for cutting. Top of tool using ";
                if(fmobj->dflag[1]>-90.)
                    cout << "rake angle " << fmobj->dflag[1];
                else
                    cout << "calculated normals";
                cout << ". Bottom of tool normal = (0,1)." << endl;
                break;
			case 5:
				cout << "   Radial normal for spherical inclusion" <<endl;
				break;
            default:
				cout << "   Unknown, or no longer implemented, custom contact option" << endl;
                break;
        }
    }
	
}
Example #19
0
// Adjust change in momentum for frictional contact in tangential direction
// If has component of tangential motion, calculate force depending on whether it is sticking or sliding
// When frictional sliding, find tangential force (times dt) and set flag, if not set flag false
// When friction heating is on, set Ftdt term and set hasFriction to true
//     (hasFriction (meaning has frictional heating value) must be initialized to false when called)
// contactArea only provided if frictional law needs it
// Normally in contact when called, but some laws might want call even when not in contact. If return
//		value is false, the contact should be treated as no contact
bool CoulombFriction::GetFrictionalDeltaMomentum(Vector *delPi,Vector *norm,double dotn,double *mredDE,double mred,
												 bool getHeating,double contactArea,bool inContact,double deltime,Vector *at) const
{
	// indicate no frictional heat yet
	*mredDE=-1.;
	
	// stick and frictionless are easy and no heating
	if(frictionStyle==STICK)
	{	// stick conditions no change
		return true;
	}
	
	else if(frictionStyle==FRICTIONLESS)
	{	// remove tangential term
		CopyScaleVector(delPi,norm,dotn);
		return true;
	}
	
	// Rest implements friction sliding
	// The initial delPi = (-N A dt) norm + (Sstick A dt) tang = dotn norm + dott tang
	// where N is normal traction (positive in compression), A is contact area, and dt is timestep
		
    // get unnormalized tangential vector and its magnitude
	// tang = delPi - dotn norm
    Vector tang;
    CopyVector(&tang,delPi);
    AddScaledVector(&tang,norm,-dotn);
    double tangMag = sqrt(DotVectors(&tang,&tang));
    
    // if has tangential motion, we need to change momemtum if frictional sliding is occuring
    if(!DbleEqual(tangMag,0.))
    {	ScaleVector(&tang,1./tangMag);
        double dott = DotVectors(delPi,&tang);
        
        // make it positive for comparison to the positive frictional force Sslide
        if(dott < 0.)
        {	ScaleVector(&tang,-1.);
            dott = -dott;
        }
		
		// Let frictional sliding force be Sslide Ac dt = f(N) Ac dt
		// Then if dott > Sslide Ac dt (which means Sstick>Sslide)
		//	a. Set delPi = dotn norm + Sslide Ac dt tang
		//      For example, Coulomb friction has Fslide dt = mu(dotn) so delPi = (norm - mu tang) dotn
		double SslideAcDt = GetSslideAcDt(-dotn,dott,0.,mred,contactArea,inContact,deltime);
		if(!inContact) return false;
		
		if(dott > SslideAcDt)
		{	CopyScaleVector(delPi,norm,dotn);
			AddScaledVector(delPi,&tang,SslideAcDt);
			
            // get frictional heating term as friction work times reduced mass
            // As heat source need Energy/sec or divide by timestep*reduced mass
            // Note: only add frictional heating during momentum update (when friction
            //   force is appropriate) and only if frictional contact heat is enabled.
            if(getHeating)
			{	if(at!=NULL)
				{	double Vs = SslideAcDt*(dott-SslideAcDt);
					AddScaledVector(at, delPi, -1./deltime);
					double AsDt = SslideAcDt*DotVectors(at,&tang)*deltime;
					if(AsDt>Vs)
					{	//*mredDE = Vs*(Vs/AsDt-1.)+0.5*AsDt;
						*mredDE = 0.5*Vs*Vs/AsDt;
					}
					else
						*mredDE = Vs - 0.5*AsDt;
				}
				else
					*mredDE = SslideAcDt*(dott-SslideAcDt);
           }
		}
    }
	
	// still in contact
	return true;
}
Example #20
0
// Print contact law settings for cracks and finalize variables
void CrackSurfaceContact::MaterialOutput(void)
{
	char hline[200];
	
	// Global material contact
	if(materialFriction<-10.)
	{   materialContactLaw=NOCONTACT;
		sprintf(hline,"contact nodes revert to center of mass velocity field");
	}
	else if(materialFriction<-.5)
	{   materialContactLaw=STICK;
		sprintf(hline,"stick conditions");
	}
	else if(DbleEqual(materialFriction,(double)0.))
	{   materialContactLaw=FRICTIONLESS;
		sprintf(hline,"frictionless sliding");
	}
	else if(materialFriction>10.)
	{   materialContactLaw=IMPERFECT_INTERFACE;
		if(materialDnc<-100.) materialDnc=materialDn;
		sprintf(hline,"imperfect interface\n     Dnt = %g MPa/mm, Dnc = %g MPa/mm, Dt = %g MPa/mm",
				materialDn,materialDnc,materialDt);
	}
	else
	{   materialContactLaw=FRICTIONAL;
		sprintf(hline,"frictional with coefficient of friction: %.6f",materialFriction);
	}
	
	// print results
	char join[3];
	join[0]=0;
	cout << "Default Contact: " << hline << endl;
	cout << "Contact Detection: ";
	if(materialContactVmin>0.)
	{	cout << "(Vrel >= " << materialContactVmin << ")";
		strcpy(join," & ");
	}
#ifdef _VELOCITY_ONLY_
	cout << join << "(Normal dv < 0)" << endl;
#else
	cout << join << "(Normal dv < 0)";
	strcpy(join," & ");
	if(displacementCheck)
	{	cout << join << "(Normal cod < 0)" << endl;
        mpmgrid.OutputContactByDisplacements();
	}
	else
		cout << endl;
#endif
	
	cout << "Normal Calculation: ";
	switch(materialNormalMethod)
	{	case MAXIMUM_VOLUME_GRADIENT:
			cout << " gradient of material or paired material (if all nonrigid), or the rigid material (if" << endl;
			cout << "                   one rigid material), that has highest magnitude. When has rigid" << endl;
			cout << "                   material, prefer rigid material gradient with bias factor = " << rigidGradientBias;
			rigidGradientBias*=rigidGradientBias;       // squared as needed in algorithm
			break;
		case MAXIMUM_VOLUME:
			cout << " gradient of material with maximum volume";
			break;
		case AVERAGE_MAT_VOLUME_GRADIENTS:
			cout << " volume-weighted mean gradient of material and other materials lumped (if all nonrigid)," << endl;
			cout << "                   on just the rigid material (if one rigid material). When has rigid" << endl;
			cout << "                   material, prefer rigid material gradient with bias factor = " << rigidGradientBias;
			rigidGradientBias*=rigidGradientBias;       // squared as needed in algorithm
			break;
		case EACH_MATERIALS_MASS_GRADIENT:
			cout << " each material's own mass gradient";
			break;
		default:
			break;
	}
	cout << endl;
    
    // development flags for multimaterial contact
    if(fmobj->dflag[0] > 0)
    {   cout << "** Development flag for custom contact **" << endl;
        int normAxis = abs(fmobj->dflag[1]);
        switch(fmobj->dflag[0])
		{	case 3:
                if(normAxis==1 || normAxis==2 || normAxis==3)
                    cout << "   Specified normal along axis " << fmobj->dflag[1] << " with +/- 1,2,3 for +/- x,y,z" << endl;
                else
                    cout << "   Specified normal = (1,0,0) rotated " << fmobj->dflag[1] << " degrees CW about z axis" << endl;
                break;
            case 4:
                cout << "   Special normals for cutting. Top of tool using rake angle " << fmobj->dflag[1] <<
                ". Bottom of tool normal = (0,1)." << endl;
                break;
			case 5:
				cout << "   Radial normal for spherical inclusion" <<endl;
				break;
            default:
                break;
        }
    }
	
}
Example #21
0
// Hardening term : find df^(alpha) . h = (-g'(alpha))*(-h)
// Here g(alpha) = 1 + Khard alpha^nhard
//    and therefore g'(alpha) = nhard Khard alpha^(nhard-1) or just Khard if nhard=1
double HillPlastic::GetDfAlphaDotH(MPMBase *mptr,int np,AnisoHardProperties *p) const
{	return DbleEqual(nhard,1.) ? Khard*p->minush :
	Khard*nhard*pow(p->aint,nhard-1)*p->minush ;
}
Example #22
0
// mesh two overlapping paths into interface elements
const char *Area::MeshInterface(void)
{
	// must be interface elements
	if(!theElems->InterfaceElements())
		return "Interfaces can only be meshed into interface elements.";
		
	// must mesh into area before an interface
	if(edges[0]->face==0 || edges[1]->face==0)
		return "An interface elements path has not yet be meshed into elements.";
	
	// can not interface an interior path or one already with an interface
	if(edges[0]->face<0 || edges[1]->face<0)
		return "An interface elements is not on a boundary (i.e., it is an interior path).";

	// paths are already oriented in same direction (not ccw around interface) - verify more
	
	// same intervals
	if(edges[0]->intervals!=edges[1]->intervals)
		return "Two interface paths have different numbers of nodes.";
	
	// same ratio
	if(!DbleEqual(edges[0]->ratio,1./edges[1]->ratio))
		return "The nodes on two interface paths are at different locations.";
		
	// set faces to -face  to indicate path used twice - Area and an Interface
	edges[0]->face=-edges[0]->face;
	edges[1]->face=-edges[1]->face;
				
	// add the interface elements - 1 for each path interval
	int i,numInt=edges[0]->intervals,rInt;
	int eNode[MaxElNd];
	int lnameEl=theElems->CurrentElemID();
	for(i=1;i<=numInt;i++)
	{	// nodes 1 and 2 or 1, 2, and 3 on first path
		if(lnameEl==LINEAR_INTERFACE)
		{	eNode[1]=edges[0]->nodeAtIndex(i);
			eNode[2]=edges[0]->nodeAtIndex(i+1);
		}
		else
		{	eNode[1]=edges[0]->nodeAtIndex(2*i-1);
			eNode[2]=edges[0]->nodeAtIndex(2*i);
			eNode[3]=edges[0]->nodeAtIndex(2*i+1);
		}

		// nodes 3 and 4 or 4, 5, and 6 on second path (in reverse order) */
		rInt=numInt-i+1;
		if(lnameEl==LINEAR_INTERFACE)
		{	eNode[3]=edges[1]->nodeAtIndex(rInt);
			eNode[4]=edges[1]->nodeAtIndex(rInt+1);
		}
		else
		{	eNode[4]=edges[1]->nodeAtIndex(2*rInt-1);
			eNode[5]=edges[1]->nodeAtIndex(2*rInt);
			eNode[6]=edges[1]->nodeAtIndex(2*rInt+1);
		}
		
		// add the element
		if(!theElems->MeshElement(eNode,mat,(double)0.,thick))
			return "Unable to create elements in the interface area";
	}
	
	return NULL;
}
/*	Calculate change in momentum when there is contact. Return true or false if an adjustment was calculated
	If BC at the node, the delta momemtum should be zero in fixed direction
	Only called if both verified are verified and have 1 or more particles
	This method should ignore material that are ignoring cracks
*/
bool CrackSurfaceContact::GetDeltaMomentum(NodalPoint *np,Vector *delPa,CrackVelocityField *cva,CrackVelocityField *cvb,
											Vector *normin,int number,bool postUpdate,double deltime,int *inContact)
{
	// first determine if there is contact
	*inContact=IN_CONTACT;
	
	// velocities above and below
	bool hasParticles;
	double massa,massb;
	Vector pka=cva->GetCMatMomentum(hasParticles,&massa);
	Vector pkb=cvb->GetCMatMomentum(hasParticles,&massb);
	double mnode=1./(massa+massb);
	
	// screen low masses
	double aratio=massa*mnode;
	if(aratio<1.e-6 || aratio>0.999999) return false;
	//if(aratio<1.e-3 || aratio>0.999) return FALSE;
	
	// find Delta p_a (see notes)
	CopyScaleVector(delPa,&pkb,massa*mnode);
	AddScaledVector(delPa,&pka,-massb*mnode);
	
	// get normalized normal vector and find Delta p_a . n (actual (vb-va).n = dotn*(ma+mb)/(ma*mb))
	Vector norm;
	CopyScaleVector(&norm,normin,1./sqrt(DotVectors2D(normin,normin)));
	double dotn=DotVectors2D(delPa,&norm);
	
	// With the first check, any movement apart will be taken as noncontact
	// Also, frictional contact assume dvel<0.
	if(dotn>=0.)
		*inContact=SEPARATED;
	else
	{	// if approaching, check displacements
        // (Note: to use only velocity, skip the following displacement check)
		Vector dispa=cva->GetCMDisplacement(np,true);
		dispa.x/=massa;
		dispa.y/=massa;
		Vector dispb=cvb->GetCMDisplacement(np,true);
		dispb.x/=massb;
		dispb.y/=massb;
		
		// normal cod
		double dnorm=(dispb.x-dispa.x)*norm.x + (dispb.y-dispa.y)*norm.y
                        - mpmgrid.GetNormalCODAdjust(&norm,NULL,0);
		if(postUpdate)
		{	double dvel=(massa+massb)*dotn/(massa*massb);
			dnorm+=dvel*deltime;
		}
		
		// if current displacement positive then no contact
		if(dnorm >= 0.) *inContact=SEPARATED;
	}
	
	// if separated, then no contact unless possibly needed for an imperfect interface
	if(*inContact==SEPARATED && CrackContactLaw[number].law!=IMPERFECT_INTERFACE) return false;
	
	// Now need to change momentum. For imperfect interface, may or may not need a change
	Vector tang;
	double dott,mu;
	
    switch(CrackContactLaw[number].law)
    {	case STICK:
            break;
			
        case FRICTIONLESS:
			CopyScaleVector(delPa,&norm,dotn);
            break;
			
        case FRICTIONAL:
			CopyVector(&tang,delPa);
			AddScaledVector(&tang,&norm,-dotn);
			dott=sqrt(DotVectors2D(&tang,&tang));
			if(!DbleEqual(dott,0.))
			{	ScaleVector(&tang,1./dott);
				dott=DotVectors2D(delPa,&tang);
				if(dott<0.)
				{	ScaleVector(&tang,-1.);
					dott=-dott;
				}
				mu=-CrackContactLaw[number].friction;
				if(dott>mu*dotn)
				{	AddScaledVector(&norm,&tang,mu);
					CopyScaleVector(delPa,&norm,dotn);
                    
                    // get frictional heating part - this is g mm^2/sec^2 = nJ
                    // Note: only add frictional heating during momentum update (when frictional
                    //   force is appropriate) and only if conduction is on.
                    if(postUpdate && ConductionTask::crackContactHeating)
                    {   if(np->NodeHasNonrigidParticles())
                        {   Vector Ftdt;
                            CopyScaleVector(&Ftdt,&tang,mu*dotn);
                            double qrate = (massa+massb)*DotVectors2D(&Ftdt,delPa)/(massa*massb);
                            
                            // As heat source need nJ/sec or multiply by 1/timestep
                            // Note that this is after transport rates are calculated (by true in last parameter)
                            conduction->AddFluxCondition(np,fabs(qrate/deltime),true);
                        }
                    }
				}
			}
            break;
			
		case IMPERFECT_INTERFACE:
			// Contact handled here only perfect interface (Dt or Dn < 0)
			// Imperfect interfaces are handled as forces later
			if(CrackContactLaw[number].Dt<0)
			{	if( (*inContact==SEPARATED && CrackContactLaw[number].Dn>=0.) ||
				   (*inContact==IN_CONTACT && CrackContactLaw[number].Dnc>=0.) )
				{	// prefect in tangential, but imperfect in normal direction
					// make stick in tangential direction only
					AddScaledVector(delPa,&norm,-dotn);
				}
				// else perfect in both so return with the stick conditions already in delPa
			}
			else if( (*inContact==SEPARATED && CrackContactLaw[number].Dn<0.) ||
					(*inContact==IN_CONTACT && CrackContactLaw[number].Dnc<0.) )
			{	// perfect in normal direction, but imperfect in tangential direction
				// make stick in normal direction only
				CopyScaleVector(delPa,&norm,dotn);
			}
			else
			{	// no change in momentum, just imperfect interface forces later and nothing changed here
				return false;
			}
			break;
			
        default:
            break;
    }
	
	return true;
}