/* 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; }
// 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; }
// 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; }
// 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; }
// 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; }
// 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; }
// 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; }
// 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); }
// 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; } }
// 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); }
/* 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; } }
// 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; }
// 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; } } }
// 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; }
// 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; } } }
// 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 ; }
// 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; }