// hold or reverse the direction (should only be done for rigid material particles) // holdFirst == true, store velocity in acc and zero the velocity // holdFirst == false, if holding, reverse using stored velocity otherwise just reverse void MPMBase::ReverseParticle(bool holdFirst,bool holding) { if(holdFirst) { acc = vel; ZeroVector(&vel); } else if(holding) { CopyScaleVector(&vel,&acc,-1.); ZeroVector(&acc); } else { vel.x=-vel.x; vel.y=-vel.y; vel.z=-vel.z; } }
// 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; }
/* 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; }
// Calculate CPDI nodes, optional shape functions and optional gradients // numDnds - number of nodes in the particle domain // cpdi[i]->ncpos - array of coordinates for corner nodes (numDnds of them) // cpdi[i]->inElem - the element they are in // cpdi[i]->ws - shape function weights (numNds of them) // cpdi[i]->wg - gradient weights (numNds of them) // throws CommonException() if too many CPDI nodes int ElementBase::GetCPDIFunctions(int *nds,double *fn,double *xDeriv,double *yDeriv,double *zDeriv,MPMBase *mpmptr) const { int i,j,numnds; CPDIDomain **cpdi = mpmptr->GetCPDIInfo(); // Need 8X8 for linear CPDI in 3D, 8X4 for linear in 2D and 9X4 for quadratic in 2D (max is 64) int cnodes[64],ncnds=0; // corner nodes and counter for those nodes double wsSi[64]; // hold ws * Si(xa) Vector wgSi[64]; // hold wg * Si(xa) // loop over the domain nodes for(i=0;i<numCPDINodes;i++) { // get straight grid shape functions ElementBase *elem = theElements[cpdi[i]->inElem]; elem->GetNodes(&numnds,nds); elem->ShapeFunction(&cpdi[i]->ncpos,FALSE,&fn[1],NULL,NULL,NULL); // loop over shape grid shape functions and collect in arrays for(j=1;j<=numnds;j++) { cnodes[ncnds] = nds[j]; wsSi[ncnds] = cpdi[i]->ws*fn[j]; if(xDeriv!=NULL) CopyScaleVector(&wgSi[ncnds], &cpdi[i]->wg, fn[j]); ncnds++; } } /* if(xDeriv!=NULL) { cout << "Initial:" << endl; for(i=0;i<ncnds;i++) { cout << "# node = " << cnodes[i] << ", ws*Si = " << wsSi[i] << ", wgx*Si = " << wgSi[i].x << ", wgy*Si = " << wgSi[i].y << endl; } } */ // shell sort by node numbers in cnodes[] (always 16 for linear CPDI) int lognb2=(int)(log((double)ncnds)*1.442695022+1.0e-5); // log base 2 int k=ncnds,l,cmpNode; double cmpWsSi; Vector cmpWgSi; for(l=1;l<=lognb2;l++) { k>>=1; // divide by 2 for(j=k;j<ncnds;j++) { i=j-k; cmpNode = cnodes[j]; cmpWsSi = wsSi[j]; if(xDeriv!=NULL) cmpWgSi = wgSi[j]; // Back up until find insertion point while(i>=0 && cnodes[i]>cmpNode) { cnodes[i+k] = cnodes[i]; wsSi[i+k] = wsSi[i]; if(xDeriv!=NULL) wgSi[i+k] = wgSi[i]; i-=k; } // Insert point cnodes[i+k]=cmpNode; wsSi[i+k]=cmpWsSi; if(xDeriv!=NULL) wgSi[i+k]=cmpWgSi; } } /* if(xDeriv!=NULL) { for(j=1;j<ncnds;j++) { if(cnodes[j]<cnodes[j-1]) { #pragma omp critical (output) { cout << "Not Sorted: " << endl; for(i=0;i<ncnds;i++) { cout << "# node = " << cnodes[i] << ", ws*Si = " << wsSi[i] << ", wgx*Si = " << wgSi[i].x << ", wgy*Si = " << wgSi[i].y << endl; } } break; } } } */ // compact same node number int count = 0; nds[0] = -1; fn[0] = 1.; for(i=0;i<ncnds;i++) { if(cnodes[i] == nds[count]) { fn[count] += wsSi[i]; if(xDeriv!=NULL) { xDeriv[count] += wgSi[i].x; yDeriv[count] += wgSi[i].y; zDeriv[count] += wgSi[i].z; } } else { if(fn[count]>1.e-10) { count++; // keep only if shape is nonzero if(count>=maxShapeNodes) { cout << "# Found " << count-1 << " nodes; need room for remaining nodes:" << endl; for(j=i;j<ncnds;j++) { cout << "# node = " << cnodes[j] << ", ws*Si = " << endl; } throw CommonException("Too many CPDI nodes found; increase maxShapeNodes in source code by at least number of remaining nodes","ElementBase::GetCPDIFunctions"); } } nds[count] = cnodes[i]; fn[count] = wsSi[i]; if(xDeriv!=NULL) { xDeriv[count] = wgSi[i].x; yDeriv[count] = wgSi[i].y; zDeriv[count] = wgSi[i].z; } } } if(fn[count]<=1.e-10) count--; return count; }
/* 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(crackContactLaw[number]->ContactIsDone(*inContact==IN_CONTACT)) return false; // Now need to change momentum. For imperfect interface, change only for perfect directions double mredDE; if(crackContactLaw[number]->IsFrictionalContact()) { bool getHeating = postUpdate && ConductionTask::crackContactHeating; double mred = (massa*massb)/(massa+massb); double contactArea = 1.; if(crackContactLaw[number]->FrictionLawNeedsContactArea()) { // Angled path correction (2D only) double dist = mpmgrid.GetPerpendicularDistance(&norm, NULL, 0.); // Area correction method (new): sqrt(2*vmin/vtot)*vtot/dist = sqrt(2*vmin*vtot)/dist double vola = cva->GetVolumeNonrigid(true),volb = cvb->GetVolumeNonrigid(true),voltot=vola+volb; contactArea = sqrt(2.0*fmin(vola,volb)*voltot)/dist; if(fmobj->IsAxisymmetric()) contactArea *= np->x; } if(!crackContactLaw[number]->GetFrictionalDeltaMomentum(delPa,&norm,dotn,&mredDE,mred, getHeating,contactArea,*inContact==IN_CONTACT,deltime,NULL)) { return false; } if(mredDE>0.) { double qrate = mredDE/mred; NodalPoint::frictionWork += qrate; // 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); } } else { // Contact handled here only perfect interface (Dt or Dn < 0) // Imperfect interfaces are handled as forces later if(crackContactLaw[number]->IsPerfectTangentialInterface()) { if(!crackContactLaw[number]->IsPerfectNormalInterface(*inContact==IN_CONTACT)) { // 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(crackContactLaw[number]->IsPerfectNormalInterface(*inContact==IN_CONTACT)) { // 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; } } return true; }