// Update particle position, velocity, temp, and conc // throws CommonException() void UpdateParticlesTask::Execute(void) { CommonException *upErr = NULL; #ifdef CONST_ARRAYS int ndsArray[MAX_SHAPE_NODES]; double fn[MAX_SHAPE_NODES]; #else int ndsArray[maxShapeNodes]; double fn[maxShapeNodes]; #endif // Damping terms on the grid or on the particles // particleAlpha = (1-beta)/dt + pdamping(t) // gridAlpha = -m*(1-beta)/dt + damping(t) double particleAlpha = bodyFrc.GetParticleDamping(mtime); double gridAlpha = bodyFrc.GetDamping(mtime); // nonPICGridAlpha = damping(t) // globalPIC = (1-beta)/dt double nonPICGridAlpha = bodyFrc.GetNonPICDamping(mtime); double globalPIC = bodyFrc.GetPICDamping(); // Update particle position, velocity, temp, and conc #pragma omp parallel for private(ndsArray,fn) for(int p=0;p<nmpmsNR;p++) { MPMBase *mpmptr = mpm[p]; try { // get shape functions const ElementBase *elemRef = theElements[mpmptr->ElemID()]; int *nds = ndsArray; elemRef->GetShapeFunctions(fn,&nds,mpmptr); int numnds = nds[0]; // Update particle position and velocity const MaterialBase *matRef=theMaterials[mpmptr->MatID()]; int matfld=matRef->GetField(); // Allow material to override global settings double localParticleAlpha = particleAlpha; double localGridAlpha = gridAlpha; matRef->GetMaterialDamping(localParticleAlpha,localGridAlpha,nonPICGridAlpha,globalPIC); // data structure for extrapolations GridToParticleExtrap *gp = new GridToParticleExtrap; // acceleration on the particle gp->acc = mpmptr->GetAcc(); ZeroVector(gp->acc); // extrapolate nodal velocity from grid to particle ZeroVector(&gp->vgpnp1); // only two possible transport tasks double rate[2]; rate[0] = rate[1] = 0.; int task; TransportTask *nextTransport; // Loop over nodes for(int i=1;i<=numnds;i++) { // increment velocity and acceleraton const NodalPoint *ndptr = nd[nds[i]]; short vfld = (short)mpmptr->vfld[i]; // increment ndptr->IncrementDelvaTask5(vfld,matfld,fn[i],gp); #ifdef CHECK_NAN // conditionally compiled check for nan velocities if(gp->vgpnp1.x!=gp->vgpnp1.x || gp->vgpnp1.y!=gp->vgpnp1.y || gp->vgpnp1.z!=gp->vgpnp1.z) { #pragma omp critical (output) { cout << "\n# UpdateParticlesTask::Execute: bad material velocity field for vfld=" << vfld << "matfld=" << matfld << " fn[i]=" << fn[i] << endl;; PrintVector("# Particle velocity vgpn1 = ",&gp->vgpnp1); cout << endl; ndptr->Describe(); } } #endif // increment transport rates nextTransport=transportTasks; task=0; while(nextTransport!=NULL) nextTransport=nextTransport->IncrementTransportRate(ndptr,fn[i],rate[task++]); } // Find grid damping acceleration parts = ag*Vgp(n) = ag*(Vgp(n+1) - Agp(n)*dt) Vector accExtra = gp->vgpnp1; AddScaledVector(&accExtra, gp->acc, -timestep); ScaleVector(&accExtra,localGridAlpha); // update position, and must be before velocity update because updates need initial velocity // This section does second order update mpmptr->MovePosition(timestep,&gp->vgpnp1,&accExtra,localParticleAlpha); // update velocity in mm/sec mpmptr->MoveVelocity(timestep,&accExtra); // update transport values nextTransport=transportTasks; task=0; while(nextTransport!=NULL) nextTransport=nextTransport->MoveTransportValue(mpmptr,timestep,rate[task++]); // energy coupling here adds adiabtic temperature rise if(ConductionTask::adiabatic) { double dTad = mpmptr->GetBufferClear_dTad(); // in K mpmptr->pTemperature += dTad; // in K } // delete grid to particle extrap data delete gp; } catch(CommonException& err) { if(upErr==NULL) { #pragma omp critical (error) upErr = new CommonException(err); } } catch(std::bad_alloc&) { if(upErr==NULL) { #pragma omp critical (error) upErr = new CommonException("Memory error","UpdateParticlesTask::Execute"); } } catch(...) { if(upErr==NULL) { #pragma omp critical (error) upErr = new CommonException("Unexpected error","UpdateParticlesTask::Execute"); } } } // throw any errors if(upErr!=NULL) throw *upErr; // rigid materials move at their current velocity for(int p=nmpmsNR;p<nmpms;p++) { mpm[p]->MovePosition(timestep); } }
// Get mass matrix, find dimensionless particle locations, // and find grid momenta void InitializationTask::Execute(void) { CommonException *initErr = NULL; // Zero Mass Matrix and vectors warnings.BeginStep(); int tp = fmobj->GetTotalNumberOfPatches(); #pragma omp parallel { // zero all nodal variables on real nodes #pragma omp for for(int i=1;i<=nnodes;i++) nd[i]->InitializeForTimeStep(); // zero ghost nodes in patch for this thread int pn = GetPatchNumber(); patches[pn]->InitializeForTimeStep(); // particle calculations get xipos for particles and if doing CPDI // precalculate CPDI info needed for subsequent shape functions #pragma omp for nowait for(int p=0;p<nmpmsRC;p++) { MPMBase *mpmptr = mpm[p]; // pointer const ElementBase *elref = theElements[mpmptr->ElemID()]; // element containing this particle try { elref->GetShapeFunctionData(mpmptr); } catch(CommonException err) { if(initErr==NULL) { #pragma omp critical initErr = new CommonException(err); } } } } // was there an error? if(initErr!=NULL) throw *initErr; // allocate crack and material velocity fields needed for time step on real nodes // tried critical sections when nodes changed, but it was slower // can't use ghost nodes, because need to test all on real nodes if(firstCrack!=NULL || maxMaterialFields>1) { #pragma omp parallel { int nds[maxShapeNodes]; double fn[maxShapeNodes]; //for(int pn=0;pn<tp;pn++) { int pn = GetPatchNumber(); // do non-rigid and rigid contact materials in patch pn for(int block=FIRST_NONRIGID;block<=FIRST_RIGID_CONTACT;block++) { // get material point (only in this patch) MPMBase *mpmptr = patches[pn]->GetFirstBlockPointer(block); while(mpmptr!=NULL) { const MaterialBase *matID = theMaterials[mpmptr->MatID()]; // material object for this particle const int matfld = matID->GetField(); // material velocity field // get nodes and shape function for material point p const ElementBase *elref = theElements[mpmptr->ElemID()]; // element containing this particle // don't actually need shape functions, but need to screen out zero shape function // like done in subsequent tasks, otherwise node numbers will not align correctly // only think used from return are numnds and nds int numnds; elref->GetShapeFunctions(&numnds,fn,nds,mpmptr); // Add particle property to each node in the element for(int i=1;i<=numnds;i++) { // use real node in this loop NodalPoint *ndptr = nd[nds[i]]; // always zero when no cracks short vfld = 0; #ifdef COMBINE_RIGID_MATERIALS // when combining rigid particles, extrapolate all to field 0 and later // copy to other active fields if(firstCrack!=NULL && block!=FIRST_RIGID_CONTACT) #else if(firstCrack!=NULL) #endif { // in CRAMP, find crack crossing and appropriate velocity field CrackField cfld[2]; cfld[0].loc = NO_CRACK; // NO_CRACK, ABOVE_CRACK, or BELOW_CRACK cfld[1].loc = NO_CRACK; int cfound=0; Vector norm; CrackHeader *nextCrack = firstCrack; while(nextCrack!=NULL) { vfld = nextCrack->CrackCross(mpmptr->pos.x,mpmptr->pos.y,ndptr->x,ndptr->y,&norm); if(vfld!=NO_CRACK) { cfld[cfound].loc=vfld; cfld[cfound].norm=norm; #ifdef IGNORE_CRACK_INTERACTIONS cfld[cfound].crackNum=1; // appears to always be same crack, and stop when found one break; #else cfld[cfound].crackNum=nextCrack->GetNumber(); cfound++; if(cfound>1) break; // stop if found two, if there are more then two, physics will be off #endif } nextCrack=(CrackHeader *)nextCrack->GetNextObject(); } // find (and allocate if needed) the velocity field // Use vfld=0 if no cracks found if(cfound>0) { // In parallel, this is critical code #pragma omp critical { try { vfld = ndptr->AddCrackVelocityField(matfld,cfld); } catch(CommonException err) { if(initErr==NULL) initErr = new CommonException(err); } } } // set material point velocity field for this node mpmptr->vfld[i] = vfld; } // make sure material velocity field is created too if(maxMaterialFields>1 && ndptr->NeedsMatVelocityField(vfld,matfld)) { // If parallel, this is critical code #pragma omp critical { try { ndptr->AddMatVelocityField(vfld,matfld); } catch(CommonException err) { if(initErr==NULL) initErr = new CommonException(err); } } } } // next material point mpmptr = (MPMBase *)mpmptr->GetNextObject(); } } //} // end for loop when not in parallel } // was there an error? if(initErr!=NULL) throw *initErr; // copy crack and material fields on real nodes to ghost nodes if(tp>1) { for(int pn=0;pn<tp;pn++) patches[pn]->InitializationReduction(); } } // Update forces applied to particles MatPtLoadBC::SetParticleFext(mtime); // remove contact conditions CrackNode::RemoveCrackNodes(); MaterialInterfaceNode::RemoveInterfaceNodes(); // turn off isothermal ramp when done and ramp step initialization thermal.CheckDone(mtime); }
// allocate crack and material velocity fields needed for time step on real nodes // tried critical sections when nodes changed, but it was slower // can't use ghost nodes, because need to test all on real nodes // // This task only used if have cracks or in multimaterial mode void InitVelocityFieldsTask::Execute(void) { CommonException *initErr = NULL; int tp = fmobj->GetTotalNumberOfPatches(); #pragma omp parallel { int nds[maxShapeNodes]; double fn[maxShapeNodes]; int pn = GetPatchNumber(); // do non-rigid and rigid contact materials in patch pn for(int block=FIRST_NONRIGID;block<=FIRST_RIGID_CONTACT;block++) { // get material point (only in this patch) MPMBase *mpmptr = patches[pn]->GetFirstBlockPointer(block); while(mpmptr!=NULL) { const MaterialBase *matID = theMaterials[mpmptr->MatID()]; // material object for this particle const int matfld = matID->GetField(); // material velocity field // get nodes and shape function for material point p const ElementBase *elref = theElements[mpmptr->ElemID()]; // element containing this particle // don't actually need shape functions, but need to screen out zero shape function // like done in subsequent tasks, otherwise node numbers will not align correctly // only thing used from return are numnds and nds int numnds; elref->GetShapeFunctions(&numnds,fn,nds,mpmptr); // Only need to decipher crack velocity field if has cracks (firstCrack!=NULL) // and if this material allows cracks. #ifdef COMBINE_RIGID_MATERIALS bool decipherCVF = firstCrack!=NULL && block!=FIRST_RIGID_CONTACT; #else bool decipherCVF = firstCrack!=NULL; #endif // Check each node for(int i=1;i<=numnds;i++) { // use real node in this loop NodalPoint *ndptr = nd[nds[i]]; // always zero when no cracks (or when ignoring cracks) short vfld = 0; // If need, find vlocity field and for each field set location // (above or below crack) and crack number (1 based) or 0 for NO_CRACK if(decipherCVF) { // in CRAMP, find crack crossing and appropriate velocity field CrackField cfld[2]; cfld[0].loc = NO_CRACK; // NO_CRACK=0, ABOVE_CRACK=1, or BELOW_CRACK=2 cfld[1].loc = NO_CRACK; int cfound=0; Vector norm; // track normal vector for crack plane CrackHeader *nextCrack = firstCrack; while(nextCrack!=NULL) { vfld = nextCrack->CrackCross(mpmptr->pos.x,mpmptr->pos.y,ndptr->x,ndptr->y,&norm); if(vfld!=NO_CRACK) { cfld[cfound].loc=vfld; cfld[cfound].norm=norm; #ifdef IGNORE_CRACK_INTERACTIONS // appears to always be same crack, and stop when found one cfld[cfound].crackNum=1; break; #endif // Get crack number (default code does not ignore interactions) cfld[cfound].crackNum=nextCrack->GetNumber(); cfound++; // stop if found two because code can only handle two interacting cracks // It exits loop now to go ahead with the first two found, by physics may be off if(cfound>1) break; } nextCrack=(CrackHeader *)nextCrack->GetNextObject(); } // find (and allocate if needed) the velocity field // Use vfld=0 if no cracks found if(cfound>0) { // In parallel, this is critical code #pragma omp critical { try { vfld = ndptr->AddCrackVelocityField(matfld,cfld); } catch(CommonException err) { if(initErr==NULL) initErr = new CommonException(err); } } } // set material point velocity field for this node mpmptr->vfld[i] = vfld; } // make sure material velocity field is created too // (Note: when maxMaterialFields==1 (Singe Mat Mode), mvf[0] is always there // so no need to create it here) if(maxMaterialFields>1 && ndptr->NeedsMatVelocityField(vfld,matfld)) { // If parallel, this is critical code #pragma omp critical { try { ndptr->AddMatVelocityField(vfld,matfld); } catch(CommonException err) { if(initErr==NULL) initErr = new CommonException(err); } } } } // next material point mpmptr = (MPMBase *)mpmptr->GetNextObject(); } } } // was there an error? if(initErr!=NULL) throw *initErr; // copy crack and material fields on real nodes to ghost nodes if(tp>1) { for(int pn=0;pn<tp;pn++) patches[pn]->InitializationReduction(); } }
// See if any particles have changed elements // Stop if off the grid // throws CommonException() void ResetElementsTask::Execute(void) { // update feedback damping now if needed bodyFrc.UpdateAlpha(timestep,mtime); // how many patches? int totalPatches = fmobj->GetTotalNumberOfPatches(); #ifdef PARALLEL_RESET // initialize error CommonException *resetErr = NULL; // parallel over patches #pragma omp parallel { // thread for patch pn int pn = GetPatchNumber(); try { // resetting all element types for(int block=FIRST_NONRIGID;block<=FIRST_RIGID_BC;block++) { // get first material point in this block MPMBase *mptr = patches[pn]->GetFirstBlockPointer(block); MPMBase *prevMptr = NULL; // previous one of this type in current patch while(mptr!=NULL) { int status = ResetElement(mptr); if(status==LEFT_GRID) { // particle has left the grid mptr->IncrementElementCrossings(); // enter warning only if this particle did not leave the grid before if(!mptr->HasLeftTheGridBefore()) { int result = warnings.Issue(fmobj->warnParticleLeftGrid,-1); if(result==REACHED_MAX_WARNINGS || result==GAVE_WARNING) { #pragma omp critical (output) { mptr->Describe(); } // abort if needed if(result==REACHED_MAX_WARNINGS) { char errMsg[100]; sprintf(errMsg,"Too many particles have left the grid\n (plot x displacement to see last one)."); mptr->origpos.x=-1.e6; throw CommonException(errMsg,"ResetElementsTask::Execute"); } } // set this particle has left the grid once mptr->SetHasLeftTheGridBefore(TRUE); } // bring back to the previous element ReturnToElement(mptr); } else if(status==NEW_ELEMENT && totalPatches>1) { // did it also move to a new patch? int newpn = mpmgrid.GetPatchForElement(mptr->ElemID()); if(pn != newpn) { if(!patches[pn]->AddMovingParticle(mptr,patches[newpn],prevMptr)) { throw CommonException("Out of memory storing data for particle changing patches","ResetElementsTask::Execute"); } } } else if(status==LEFT_GRID_NAN) { #pragma omp critical (output) { cout << "# Particle has left the grid and position is nan" << endl; mptr->Describe(); } throw CommonException("Particle has left the grid and position is nan","ResetElementsTask::Execute"); } // next material point and update previous particle prevMptr = mptr; mptr = (MPMBase *)mptr->GetNextObject(); } } } catch(CommonException& err) { if(resetErr==NULL) { #pragma omp critical (error) resetErr = new CommonException(err); } } catch(std::bad_alloc&) { if(resetErr==NULL) { #pragma omp critical (error) resetErr = new CommonException("Memory error","ResetElementsTask::Execute"); } } catch(...) { if(resetErr==NULL) { #pragma omp critical (error) resetErr = new CommonException("Unexepected error","ResetElementsTask::Execute"); } } } // throw now if was an error if(resetErr!=NULL) throw *resetErr; // reduction phase moves the particles for(int pn=0;pn<totalPatches;pn++) patches[pn]->MoveParticlesToNewPatches(); #else int status; MPMBase *mptr,*prevMptr,*nextMptr; for(int pn=0;pn<totalPatches;pn++) { for(int block=FIRST_NONRIGID;block<=FIRST_RIGID_BC;block++) { // get first material point in this block mptr = patches[pn]->GetFirstBlockPointer(block); prevMptr = NULL; // previous one of this type in current patch while(mptr!=NULL) { status = ResetElement(mptr); if(status==LEFT_GRID) { // particle has left the grid mptr->IncrementElementCrossings(); // enter warning only if this particle did not leave the grid before if(!mptr->HasLeftTheGridBefore()) { int result = warnings.Issue(fmobj->warnParticleLeftGrid,-1); if(result==REACHED_MAX_WARNINGS || result==GAVE_WARNING) { mptr->Describe(); // abort if needed if(result==REACHED_MAX_WARNINGS) { char errMsg[100]; sprintf(errMsg,"Too many particles have left the grid\n (plot x displacement to see last one)."); mptr->origpos.x=-1.e6; throw CommonException(errMsg,"ResetElementsTask::Execute"); } } // set this particle has left the grid once mptr->SetHasLeftTheGridBefore(TRUE); } // bring back to the previous element ReturnToElement(mptr); } else if(status==NEW_ELEMENT && totalPatches>1) { int newpn = mpmgrid.GetPatchForElement(mptr->ElemID()); if(pn != newpn) { // next material point read before move this particle nextMptr = (MPMBase *)mptr->GetNextObject(); // move particle mptr patches[pn]->RemoveParticleAfter(mptr,prevMptr); patches[newpn]->AddParticle(mptr); // next material point is now after the prevMptr, which stays the same, which may be NULL mptr = nextMptr; continue; } } else if(status==LEFT_GRID_NAN) { cout << "# Particle has left the grid and position is nan" << endl; mptr->Describe(); throw CommonException("Particle has left the grid and position is nan","ResetElementsTask::Execute"); } // next material point and update previous particle prevMptr = mptr; mptr = (MPMBase *)mptr->GetNextObject(); } } } #endif }
//----------------------------------------------------------- // Subroutine to translate BMP file into material point //----------------------------------------------------------- void MPMReadHandler::TranslateBMPFiles(void) { unsigned char **rows,**angleRows; BMPInfoHeader info,angleInfo; bool setAngles=FALSE; int numRotations=strlen(rotationAxes); // read image file char *bmpFullPath=archiver->ExpandOutputPath(bmpFileName); ReadBMPFile(bmpFullPath,info,&rows); delete [] bmpFullPath; // angle file name (overrides other angle settings) if(bmpAngleFileName[0]>0) { setAngles=TRUE; char *bmpFullAnglePath=archiver->ExpandOutputPath(bmpAngleFileName); ReadBMPFile(bmpFullAnglePath,angleInfo,&angleRows); if(info.height!=angleInfo.height || info.width!=angleInfo.width) throw SAXException(BMPError("The image file and angle file sizes do not match.",bmpFileName)); delete [] bmpFullAnglePath; } else if(numRotations>0) { int i; for(i=0;i<numRotations;i++) { char *expr=new char[strlen(angleExpr[i])+1]; strcpy(expr,angleExpr[i]); if(!CreateFunction(expr,i+1)) throw SAXException("Invalid angle expression was provided in <RotateX(YZ)> command."); delete [] expr; } } // bheight and bwidth provided in bmp file // <-1.e8 means not provided // negative means provided mm per pixel // positive is a specified size double xpw=-1.,ypw=-1.; if(bwidth<-1.e8 && bheight<-1.e8) { if(!info.knowsCellSize) throw SAXException(BMPError("<BMP> must specify width and/or height as size or pixels per mm.",bmpFileName)); bwidth = info.width*info.xcell; bheight = info.height*info.ycell; xpw = info.xcell; ypw = info.ycell; } else { // provided mm per pixel if(bheight<0. && bheight>-1.e8) { ypw = -bheight; bheight = ypw*(double)info.height; } if(bwidth<0. && bwidth>-1.e8) { xpw = -bwidth; bwidth = xpw*(double)info.width; } } // total dimensions (if only one is known, find the other, never have both unknown) if(bheight<0) bheight=bwidth*(double)info.height/(double)info.width; if(bwidth<0) bwidth=bheight*(double)info.width/(double)info.height; // final mm per pixel (if needed) if(xpw<0.) xpw=bwidth/(double)info.width; if(ypw<0.) ypw=bheight/(double)info.height; // variables for scanning BMP file int matID; MPMBase *newMpt; int ii,k; Vector mpos[MaxElParticles]; int ptFlag; BMPLevel *nextLevel; double deltax,deltay,deltaz,semiscale; double rmin,rmax,wtr1,wtr2,rweight; double cmin,cmax,wtc1,wtc2,weight; int r1,r2,c1,c2; BMPLevel *maxLevel; int row,col; ElementBase *elem; // Length/semiscale is half particle with // (semiscale=4 for 2D w 4 pts per element or 3D with 8 pts per element, // or 2 if 1 particle in the element) if(fmobj->IsThreeD()) semiscale=2.*pow((double)fmobj->ptsPerElement,1./3.); else semiscale=2.*sqrt((double)fmobj->ptsPerElement); /* Parallelizing the following loop will speed up check meshes on Mac 1. Will need copy of levels for each block (or pass copy of weight to BMPLevel methods) see BMPLevel methods: ClearWeight(),Material(double,double), MaximumWeight(double) 2. mpCtrl->AddMaterialPoint() will need critical block 3. FunctionValue() when setting angles uses globals they are problem for parallel */ // scan mesh and assign material points or angles try { for(ii=1;ii<=nelems;ii++) { // skip if image not in extent of element box elem=theElements[ii-1]; if(!elem->IntersectsBox(xorig,yorig,bwidth,bheight,zslice)) continue; // load point coordinates elem->MPMPoints(fmobj->ptsPerElement,mpos); // half the extent of the volume of a particle deltax=(elem->GetDeltaX())/semiscale; deltay=(elem->GetDeltaY())/semiscale; if(fmobj->IsThreeD()) deltaz=elem->GetDeltaZ()/semiscale; else deltaz=1.; if(deltaz<0.) deltaz=1.; for(k=0;k<fmobj->ptsPerElement;k++) { ptFlag=1<<k; // skip if already filled if(elem->filled&ptFlag) continue; // if point in the view area, then check it if((mpos[k].x>=xorig && mpos[k].x<xorig+bwidth) && (mpos[k].y>=yorig && mpos[k].y<yorig+bheight) && (mpos[k].z>=zslice-deltaz && mpos[k].z<zslice+deltaz)) { // find range of rows and cols for pixels over this material point if(yflipped) { rmin=(yorig+bheight-mpos[k].y-deltay)/ypw; rmax=(yorig+bheight-mpos[k].y-deltay)/ypw; } else { rmin=(mpos[k].y-deltay-yorig)/ypw; rmax=(mpos[k].y+deltay-yorig)/ypw; } r1=BMPIndex(rmin,info.height); r2=BMPIndex(rmax,info.height); if(r2==r1) { wtr1=wtr2=1.; } else { // fractional weight for first and last row in case not all within the extent wtr1=(double)(r1+1)-rmin; wtr2=rmax-(double)r2; } cmin=(mpos[k].x-deltax-xorig)/xpw; cmax=(mpos[k].x+deltax-xorig)/xpw; c1=BMPIndex(cmin,info.width); c2=BMPIndex(cmax,info.width); if(c2==c1) { wtc1=wtc2=1.; } else { // fractional weight for first and last col in case not all within the extent wtc1=(double)(c1+1)-cmin; wtc2=cmax-(double)c2; } // find material ID or none (a hole) // clear material weights nextLevel=firstLevel; while(nextLevel!=NULL) nextLevel=nextLevel->ClearWeight(); // add weights for(row=r1;row<=r2;row++) { if(row==r1) rweight=wtr1; else if(row==r2) rweight=wtr2; else rweight=1.; for(col=c1;col<=c2;col++) { if(col==c1) weight=rweight*wtc1; else if(col==c2) weight=rweight*wtc2; else weight=rweight; // find material at this level // (note: last level with matID=0 catches empty space) nextLevel=firstLevel; while(nextLevel!=NULL) { matID=nextLevel->Material(rows[row][col],weight); if(matID>=0) break; nextLevel=(BMPLevel *)nextLevel->GetNextObject(); } } } // find maximum weight (matID=0 means max is empty space) weight=0.; maxLevel=NULL; nextLevel=firstLevel; while(nextLevel!=NULL) nextLevel=nextLevel->MaximumWeight(weight,&maxLevel); if(maxLevel!=NULL) { matID=maxLevel->mat; nextLevel=maxLevel; } else matID=-1; // create a material point if one at this spot using matID and nextLevel // Note that empty spaced is not marked as filled which allows superposition of // images with different materials. If want to forcefully create a hole that // cannot be filled by subsequent image, will need to define a new material // type that can have matID for a hole. It will not create a point, but will mark // the location as filled if(matID>0) { if(fmobj->IsThreeD()) newMpt=new MatPoint3D(ii,matID,nextLevel->angle); else if(fmobj->IsAxisymmetric()) newMpt=new MatPointAS(ii,matID,nextLevel->angle,mpos[k].x); else newMpt=new MatPoint2D(ii,matID,nextLevel->angle,nextLevel->thickness); newMpt->SetPosition(&mpos[k]); newMpt->SetOrigin(&mpos[k]); newMpt->SetVelocity(&nextLevel->vel); mpCtrl->AddMaterialPoint(newMpt,nextLevel->concentration,nextLevel->temperature); // is there an angle image? if(setAngles) { // weight average of scanned pixels double totalWeight=0.; double totalIntensity=0.; for(row=r1;row<=r2;row++) { if(row==r1) rweight=wtr1; else if(row==r2) rweight=wtr2; else rweight=1.; for(col=c1;col<=c2;col++) { if(col==c1) weight=rweight*wtc1; else if(col==c2) weight=rweight*wtc2; else weight=rweight; totalIntensity+=weight*angleRows[row][col]; totalWeight+=weight; } } totalIntensity/=totalWeight; double matAngle=minAngle+(totalIntensity-minIntensity)*angleScale; newMpt->SetAnglez0InDegrees(matAngle); } else { // If had Rotate commands then use them SetMptAnglesFromFunctions(numRotations,&mpos[k],newMpt); } elem->filled|=ptFlag; } } } } } catch(const char *msg) { throw SAXException(msg); } // clean up for(row=0;row<info.height;row++) { delete [] rows[row]; if(setAngles) delete [] angleRows[row]; } delete [] rows; if(setAngles) delete [] angleRows; // angles if allocated for(ii=0;ii<numRotations;ii++) { delete [] angleExpr[ii]; } DeleteFunction(-1); }
// Calculate J and K at crack tips CustomTask *PropagateTask::StepCalculation(void) { // if not needed, just exit if(!doPropCalcs) return nextTask; // particle extrapolation if needed if(doEnergyBalanceCalcs) { totalPlastic = 0.; totalPotential = 0.; for(int p=0;p<nmpmsNR;p++) { MPMBase *mpnt = mpm[p]; // track total energies in J = N-m // mp is g, stored energy is N/m^2 cm^3/g, vel is mm/sec // workEnergy in J = 1.0e-6*mp*mpm[p]->GetWorkEnergy() // plastic 1.0e-6*mp*mpm[p]->GetPlastEnergy() // external work 1.e-9*mpm[p]->GetExtWork() // kinetic energy 0.5e-9*mp*(vel.x*vel.x+vel.y*vel.y) // plastic energy per unit thickness (units of N) (only needed energy balance crack growth) double mp = mpnt->mp; totalPlastic += 1.0e-3*mp*mpnt->GetPlastEnergy()/mpnt->thickness(); //totalPotential += 1.0e-3*(mp*mpnt->GetStrainEnergy() // + 0.5e-3*mp*(mpnt->vel.x*mpnt->vel.x+mpnt->vel.y*mpnt->vel.y) // - 1.e-3*mpnt->GetExtWork())/mpnt->thickness(); throw "external work is no longer available"; } } CrackHeader *nextCrack; CrackSegment *crkTip; double cSize; int i,inMat; Vector tipDir,growTo,grow; int tipElem; char isAlt[10]; // loop over cracks nextCrack=firstCrack; while(nextCrack!=NULL) { // each crack tip for(i=START_OF_CRACK;i<=END_OF_CRACK;i++) { // find crack tip and direction nextCrack->CrackTipAndDirection(i,&crkTip,tipDir); // crack propagation inMat=crkTip->tipMatnum; if(inMat>0) { // crack tip terms last two times propagated crkTip->potential[2]=crkTip->potential[1]; crkTip->potential[1]=crkTip->potential[0]; crkTip->potential[0]=totalPotential; crkTip->plastic[2]=crkTip->plastic[1]; crkTip->plastic[1]=crkTip->plastic[0]; crkTip->plastic[0]=totalPlastic; crkTip->clength[2]=crkTip->clength[1]; crkTip->clength[1]=crkTip->clength[0]; crkTip->clength[0]=nextCrack->Length(); // see if it grows int shouldGo=theMaterials[inMat-1]->ShouldPropagate(crkTip,tipDir,nextCrack,fmobj->np,0); isAlt[0] = 0; if(shouldGo==GROWNOW) { nextCrack->SetAllowAlternate(i,FALSE); } else if(nextCrack->GetAllowAlternate(i)) { shouldGo=theMaterials[inMat-1]->ShouldPropagate(crkTip,tipDir,nextCrack,fmobj->np,1); if(shouldGo==GROWNOW) strcpy(isAlt," (alt)"); } if(shouldGo==GROWNOW) { theResult=GROWNOW; tipElem=crkTip->planeInElem-1; if(fabs(tipDir.x)>fabs(tipDir.y)) cSize=theElements[tipElem]->xmax-theElements[tipElem]->xmin; else cSize=theElements[tipElem]->ymax-theElements[tipElem]->ymin; grow.x=cellsPerPropagationStep*cSize*tipDir.x; grow.y=cellsPerPropagationStep*cSize*tipDir.y; // adjust if crossing another crack double p = nextCrack->AdjustGrowForCrossing(&grow,crkTip); // crack number and tip archiver->IncrementPropagationCounter(); cout << "# propagation" << isAlt << " crack-tip " << nextCrack->GetNumber() << "-" << i; // summarize cout << " at t=" << 1000*mtime << " with J=Jtip+Jzone : " << 1000.*crkTip->Jint.z << " = " << 1000.*crkTip->Jint.x << " + " << 1000.*(crkTip->Jint.z-crkTip->Jint.x) << endl; // if jump is .7 or more cells, make more than 1 segment int iseg,numSegs = 1; if(p*cellsPerPropagationStep>.7) numSegs= 2*(p*cellsPerPropagationStep+.25); CrackSegment *newCrkTip; for(iseg=1;iseg<=numSegs;iseg++) { growTo.x=crkTip->x+(double)iseg*grow.x/(double)numSegs; growTo.y=crkTip->y+(double)iseg*grow.y/(double)numSegs; if(fmobj->dflag[0]==4) growTo.y=0.; // force cutting simulation to stay in cut plane at 0 newCrkTip=nextCrack->Propagate(growTo,(int)i,theMaterials[inMat-1]->tractionMat[0]); } crkTip = newCrkTip; if(crkTip!=NULL) { // check if crack speed is being controlled if(theMaterials[inMat-1]->ControlCrackSpeed(crkTip,propTime)) nextPropTime=mtime+propTime; // crack tip heating (if activated) if(ConductionTask::active) conduction->StartCrackTipHeating(crkTip,grow,nextCrack->GetThickness()); } } else { // when no growth, restore previous growth results crkTip->potential[0]=crkTip->potential[1]; crkTip->potential[1]=crkTip->potential[2]; crkTip->plastic[0]=crkTip->plastic[1]; crkTip->plastic[1]=crkTip->plastic[2]; crkTip->clength[0]=crkTip->clength[1]; crkTip->clength[1]=crkTip->clength[2]; } } } // next crack nextCrack=(CrackHeader *)nextCrack->GetNextObject(); } return nextTask; }
// increment external load on a particle // input is analysis time in seconds // (only called when diffusion is active) MatPtFluxBC *MatPtFluxBC::AddMPFlux(double bctime) { // condition value is g/(mm^2-sec), Divide by rho*csat to get potential flux in mm/sec // find this flux and then add (times area) to get mm^3-potential/sec MPMBase *mpmptr = mpm[ptNum-1]; MaterialBase *matptr = theMaterials[mpmptr->MatID()]; // Flux is a scalar and we need int_(face) F Ni(x) dA // Since F is constant, only need integral which is done by CPDI methods // which has be generalized to work for GIMP too // We use X_DIRECTION for bcDIR for efficiency. For Silent BC, change to // Normal direction to all caculation of n Vector fluxMag; ZeroVector(&fluxMag); int bcDir=X_DIRECTION; if(style==SILENT) { TransportProperties t; matptr->GetTransportProps(mpmptr,fmobj->np,&t); Tensor *D = &(t.diffusionTensor); // D in mm^2/sec, Dc in 1/mm if(fmobj->IsThreeD()) { fluxMag.x = D->xx*mpmptr->pDiffusion[gGRADx] + D->xy*mpmptr->pDiffusion[gGRADy] + D->xz*mpmptr->pDiffusion[gGRADz]; fluxMag.y = D->xy*mpmptr->pDiffusion[gGRADx] + D->yy*mpmptr->pDiffusion[gGRADy] + D->yz*mpmptr->pDiffusion[gGRADz]; fluxMag.z = D->xz*mpmptr->pDiffusion[gGRADx] + D->yz*mpmptr->pDiffusion[gGRADy] + D->zz*mpmptr->pDiffusion[gGRADz]; } else { fluxMag.x = D->xx*mpmptr->pDiffusion[gGRADx] + D->xy*mpmptr->pDiffusion[gGRADy]; fluxMag.y = D->xy*mpmptr->pDiffusion[gGRADx] + D->yy*mpmptr->pDiffusion[gGRADy]; } bcDir = N_DIRECTION; } else if(direction==EXTERNAL_FLUX) { // csatrho = rho0 V0 csat/V (units g/mm^3) double csatrho = mpmptr->GetRho()*mpmptr->GetConcSaturation()/mpmptr->GetRelativeVolume(); fluxMag.x = BCValue(bctime)/csatrho; } else { // coupled surface flux and ftime is bath concentration // time variable (t) is replaced by c-cbath, where c is the particle potential and cbath is bath potential varTime = mpmptr->pPreviousConcentration-GetBCFirstTime(); GetPosition(&varXValue,&varYValue,&varZValue,&varRotValue); double currentValue = fabs(scale*function->Val()); if(varTime>0.) currentValue=-currentValue; // csatrho = rho0 V0 csat/V (units g/mm^3) double csatrho = mpmptr->GetRho()*mpmptr->GetConcSaturation()/mpmptr->GetRelativeVolume(); fluxMag.x = currentValue/csatrho; } // get corners and direction from material point int cElem[4],numDnds; Vector corners[4],tscaled; double ratio = mpmptr->GetTractionInfo(face,bcDir,cElem,corners,&tscaled,&numDnds); // compact CPDI nodes into list of nodes (nds) and final shape function term (fn) // May need up to 8 (in 3D) for each of the numDnds (2 in 2D or 4 in 3D) int nds[8*numDnds+1]; double fn[8*numDnds+1]; int numnds = CompactCornerNodes(numDnds,corners,cElem,ratio,nds,fn); // add force to each node int i; for(i=1;i<=numnds;i++) diffusion->AddFluxCondition(nd[nds[i]],DotVectors(&fluxMag,&tscaled)*fn[i],false); return (MatPtFluxBC *)GetNextObject(); }
// Calculate J and K at crack tips CustomTask *CalcJKTask::StepCalculation(void) { // skip if not needed if(!getJKThisStep) return nextTask; int nds[maxShapeNodes]; double fn[maxShapeNodes]; //double xDeriv[maxShapeNodes],yDeriv[maxShapeNodes],zDeriv[maxShapeNodes]; // set up strain fields for crack extrapolations //#pragma omp parallel private(nds,fn,xDeriv,yDeriv,zDeriv) #pragma omp parallel private(nds,fn) { // in case 2D planar //for(int i=0;i<maxShapeNodes;i++) zDeriv[i] = 0.; #pragma omp for for(int i=1;i<=nnodes;i++) nd[i]->ZeroDisp(); // zero displacement fields on ghost nodes int pn = MPMTask::GetPatchNumber(); patches[pn]->ZeroDisp(); // loop over only non-rigid particles in patch that do not ignore cracks MPMBase *mpnt = patches[pn]->GetFirstBlockPointer(FIRST_NONRIGID); while(mpnt!=NULL) { // material reference const MaterialBase *matref = theMaterials[mpnt->MatID()]; // find shape functions and derviatives int numnds; const ElementBase *elref = theElements[mpnt->ElemID()]; //elref->GetShapeGradients(&numnds,fn,nds,xDeriv,yDeriv,zDeriv,mpnt); elref->GetShapeFunctions(&numnds,fn,nds,mpnt); // Add particle property to each node in the element NodalPoint *ndmi; short vfld; double fnmp; for(int i=1;i<=numnds;i++) { // global mass matrix vfld=(short)mpnt->vfld[i]; // velocity field to use fnmp=fn[i]*mpnt->mp; // get node pointer ndmi = MPMTask::GetNodePointer(pn,nds[i]); // get 2D gradient terms (dimensionless) and track material (if needed) int activeMatField = matref->GetActiveField(); Matrix3 gradU = mpnt->GetDisplacementGradientMatrix(); ndmi->AddUGradient(vfld,fnmp,gradU(0,0),gradU(0,1),gradU(1,0),gradU(1,1),activeMatField,mpnt->mp); // GRID_JTERMS double rho = matref->GetRho(NULL); if(JGridEnergy) { // Add velocity (scaled by sqrt(rho) such that v^2 is 2 X grid kinetic energy in nJ/mm^3) // In axisymmetric, kinetic energy density is 2 pi (0.5 m v^2)/(2 pi rp Ap), but since m = rho rp Ap // kinetic energy density is still 0.5 rho v^2 ndmi->AddGridVelocity(vfld,fnmp*sqrt(rho),mpnt->vel.x,mpnt->vel.y); // scale by rho to get actual stress fnmp *= rho; } else { // scale by rho to get specific energy and actual stress fnmp *= rho; // get energy and rho*energy has units nJ/mm^3 // In axisymmetric, energy density is 2 pi m U/(2 pi rp Ap), but since m = rho rp Ap // energy density is still rho*energy ndmi->AddEnergy(vfld,fnmp,mpnt->vel.x,mpnt->vel.y,mpnt->GetWorkEnergy()); } // get a nodal stress (rho*stress has units N/m^2 = uN/mm^2) Tensor sp = mpnt->ReadStressTensor(); ndmi->AddStress(vfld,fnmp,&sp); } // next non-rigid material point mpnt = (MPMBase *)mpnt->GetNextObject(); } } // copy ghost to real nodes int totalPatches = fmobj->GetTotalNumberOfPatches(); if(totalPatches>1) { for(int j=0;j<totalPatches;j++) patches[j]->JKTaskReduction(); } // finish strain fields #pragma omp parallel for for(int i=1;i<=nnodes;i++) nd[i]->CalcStrainField(); // No Do the J Integral calculations int inMat; Vector d,C; CrackSegment *crkTip; CrackHeader *nextCrack=firstCrack; while(nextCrack!=NULL) { nextCrack->JIntegral(); // crack-axis components of J-integral // if material known, find KI and KII for crack tips if(getJKThisStep & NEED_K) { for(int i=START_OF_CRACK;i<=END_OF_CRACK;i++) { crkTip=nextCrack->GetCrackTip(i); inMat=crkTip->tipMatnum; if(inMat>0) { //C=crkTip->C; // will be crack segment property C.x=0.0; C.y=0.0; ; C.z=0.; // find normal and shear COD nextCrack->GetCOD(crkTip,d,true); d.z=0.; // convert to K crkTip->sif=theMaterials[inMat-1]->ConvertJToK(d,C,crkTip->Jint,fmobj->np); } else { crkTip->sif.x=0; crkTip->sif.y=0; } } } // next crack nextCrack=(CrackHeader *)nextCrack->GetNextObject(); } return nextTask; }
// Get mass matrix, find dimensionless particle locations, // and find grid momenta void ExtrapolateRigidBCsTask::Execute(void) { double fn[maxShapeNodes]; // undo dynamic velocity, temp, and conc BCs from rigid materials // and get pointer to first empty one in reuseRigid...BC ProjectRigidBCsTask::UnsetRigidBCs((BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); ProjectRigidBCsTask::UnsetRigidBCs((BoundaryCondition **)&firstTempBC,(BoundaryCondition **)&lastTempBC, (BoundaryCondition **)&firstRigidTempBC,(BoundaryCondition **)&reuseRigidTempBC); ProjectRigidBCsTask::UnsetRigidBCs((BoundaryCondition **)&firstConcBC,(BoundaryCondition **)&lastConcBC, (BoundaryCondition **)&firstRigidConcBC,(BoundaryCondition **)&reuseRigidConcBC); int i,numnds,setFlags; Vector rvel; bool hasDir[3]; double tempValue,concValue; // this loop not parallel because of possible function in getting rigid settings // also will usually be small loop for(int p=nmpmsRC;p<nmpms;p++) { // bet material point and the rigid material MPMBase *mpmptr = mpm[p]; const RigidMaterial *rigid = (RigidMaterial *)theMaterials[mpmptr->MatID()]; // material object for this particle // get rigid particle velocity // Get directions set, others will be zero setFlags = rigid->SetDirection(); ZeroVector(&rvel); if(rigid->GetVectorSetting(&rvel,hasDir,mtime,&mpmptr->pos)) { if(hasDir[0]) mpmptr->vel.x = rvel.x; if(hasDir[1]) mpmptr->vel.y = rvel.y; if(hasDir[2]) mpmptr->vel.z = rvel.z; } else { if(setFlags&CONTROL_X_DIRECTION) rvel.x = mpmptr->vel.x; if(setFlags&CONTROL_Y_DIRECTION) rvel.y = mpmptr->vel.y; if(setFlags&CONTROL_Z_DIRECTION) rvel.z = mpmptr->vel.z; } // get rigid particle temperature if(rigid->RigidTemperature()) { setFlags += CONTROL_TEMPERATURE; if(rigid->GetValueSetting(&tempValue,mtime,&mpmptr->pos)) mpmptr->pTemperature = tempValue; } // concentration if(rigid->RigidConcentration()) { setFlags += CONTROL_CONCENTRATION; if(rigid->GetValueSetting(&concValue,mtime,&mpmptr->pos)) mpmptr->pConcentration = concValue; } // get nodes and shape function for material point p const ElementBase *elref = theElements[mpmptr->ElemID()]; // element containing this particle elref->ShapeFunction(mpmptr->GetNcpos(),FALSE,&fn[1],NULL,NULL,NULL); numnds=elref->NumberNodes(); // Add particle property to each node in the element for(i=1;i<=numnds;i++) { // get node pointer and set values int mi=elref->nodes[i-1]; // 1 based node // it might possible need a velocity field (does nothing in single material mode or if already there) nd[mi]->AddMatVelocityField(0,0); // add BC info nd[mi]->AddRigidBCInfo(mpmptr,fn[i],setFlags,&rvel); } } // read nodal settings, rezero their used values, and set boundary conditions for(int i=1;i<=nnodes;i++) { NodalPoint *ndptr = nd[i]; setFlags = ndptr->ReadAndZeroRigidBCInfo(&rvel,&tempValue,&concValue); if(setFlags&CONTROL_X_DIRECTION) { ProjectRigidBCsTask::SetRigidBCs(i,-40,X_DIRECTION,rvel.x,0.,0, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } if(setFlags&CONTROL_X_DIRECTION) { ProjectRigidBCsTask::SetRigidBCs(i,-40,Y_DIRECTION,rvel.y,0.,0, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } if(setFlags&CONTROL_X_DIRECTION) { ProjectRigidBCsTask::SetRigidBCs(i,-40,Z_DIRECTION,rvel.z,0.,0, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } if(setFlags&CONTROL_TEMPERATURE) { ProjectRigidBCsTask::SetRigidBCs(i,0,TEMP_DIRECTION,tempValue,0.,0, (BoundaryCondition **)&firstTempBC,(BoundaryCondition **)&lastTempBC, (BoundaryCondition **)&firstRigidTempBC,(BoundaryCondition **)&reuseRigidTempBC); } if(setFlags&CONTROL_CONCENTRATION) { ProjectRigidBCsTask::SetRigidBCs(i,0,CONC_DIRECTION,concValue,0.,0, (BoundaryCondition **)&firstConcBC,(BoundaryCondition **)&lastConcBC, (BoundaryCondition **)&firstRigidConcBC,(BoundaryCondition **)&reuseRigidConcBC); } } // if any left over rigid BCs, delete them now ProjectRigidBCsTask::RemoveRigidBCs((BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC,(BoundaryCondition **)&firstRigidVelocityBC); ProjectRigidBCsTask::RemoveRigidBCs((BoundaryCondition **)&firstTempBC,(BoundaryCondition **)&lastTempBC,(BoundaryCondition **)&firstRigidTempBC); ProjectRigidBCsTask::RemoveRigidBCs((BoundaryCondition **)&firstConcBC,(BoundaryCondition **)&lastConcBC,(BoundaryCondition **)&firstRigidConcBC); }
// Get mass matrix, find dimensionless particle locations, // and find grid momenta void MassAndMomentumTask::Execute(void) { CommonException *massErr = NULL; double fn[maxShapeNodes],xDeriv[maxShapeNodes],yDeriv[maxShapeNodes],zDeriv[maxShapeNodes]; int nds[maxShapeNodes]; #pragma mark ... UPDATE RIGID CONTACT PARTICLES // Set rigid BC contact material velocities first (so loop can be parallel) // GetVectorSetting() uses globals and therefore can't be parallel if(nmpmsRC>nmpmsNR) { Vector newvel; bool hasDir[3]; for(int p=nmpmsNR;p<nmpmsRC;p++) { MPMBase *mpmptr = mpm[p]; const RigidMaterial *matID = (RigidMaterial *)theMaterials[mpm[p]->MatID()]; if(matID->GetVectorSetting(&newvel,hasDir,mtime,&mpmptr->pos)) { // change velocity if functions being used, otherwise keep velocity constant if(hasDir[0]) mpmptr->vel.x = newvel.x; if(hasDir[1]) mpmptr->vel.y = newvel.y; if(hasDir[2]) mpmptr->vel.z = newvel.z; } } } // loop over non-rigid and rigid contact particles - this parallel part changes only particle p // mass, momenta, etc are stored on ghost nodes, which are sent to real nodes in next non-parallel loop //for(int pn=0;pn<4;pn++) #pragma omp parallel private(fn,xDeriv,yDeriv,zDeriv,nds) { // thread for patch pn int pn = GetPatchNumber(); // in case 2D planar for(int i=0;i<maxShapeNodes;i++) zDeriv[i] = 0.; #pragma mark ... EXTRAPOLATE NONRIGID PARTICLES try { for(int block=FIRST_NONRIGID;block<=FIRST_RIGID_CONTACT;block++) { MPMBase *mpmptr = patches[pn]->GetFirstBlockPointer(block); while(mpmptr!=NULL) { const MaterialBase *matID = theMaterials[mpmptr->MatID()]; // material object for this particle int matfld = matID->GetField(); // material velocity field // get nodes and shape function for material point p int i,numnds; const ElementBase *elref = theElements[mpmptr->ElemID()]; // element containing this particle if(fmobj->multiMaterialMode) elref->GetShapeGradients(&numnds,fn,nds,xDeriv,yDeriv,zDeriv,mpmptr); else elref->GetShapeFunctions(&numnds,fn,nds,mpmptr); // Add particle property to each node in the element short vfld; NodalPoint *ndptr; for(i=1;i<=numnds;i++) { // get node pointer ndptr = GetNodePointer(pn,nds[i]); // momentum vector (and allocate velocity field if needed) vfld = mpmptr->vfld[i]; ndptr->AddMassMomentum(mpmptr,vfld,matfld,fn[i],xDeriv[i],yDeriv[i],zDeriv[i], 1,block==FIRST_NONRIGID); } // next material point mpmptr = (MPMBase *)mpmptr->GetNextObject(); } } } catch(CommonException err) { if(massErr==NULL) { #pragma omp critical massErr = new CommonException(err); } } catch(...) { cout << "Unknown exception in MassAndMomentumTask()" << endl; } } // throw now - only possible error if too many CPDI nodes in 3D if(massErr!=NULL) throw *massErr; // reduction of ghost node forces to real nodes int totalPatches = fmobj->GetTotalNumberOfPatches(); if(totalPatches>1) { for(int pn=0;pn<totalPatches;pn++) patches[pn]->MassAndMomentumReduction(); } #pragma mark ... RIGID BOUNDARY CONDITIONS // undo dynamic velocity, temp, and conc BCs from rigid materials // and get pointer to first empty one in reuseRigid...BC UnsetRigidBCs((BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); UnsetRigidBCs((BoundaryCondition **)&firstTempBC,(BoundaryCondition **)&lastTempBC, (BoundaryCondition **)&firstRigidTempBC,(BoundaryCondition **)&reuseRigidTempBC); UnsetRigidBCs((BoundaryCondition **)&firstConcBC,(BoundaryCondition **)&lastConcBC, (BoundaryCondition **)&firstRigidConcBC,(BoundaryCondition **)&reuseRigidConcBC); // For Rigid BC materials create velocity BC on each node in the element for(int p=nmpmsRC;p<nmpms;p++) { MPMBase *mpmptr = mpm[p]; // pointer int matid0 = mpmptr->MatID(); const MaterialBase *matID = theMaterials[matid0]; // material object for this particle RigidMaterial *rigid=(RigidMaterial *)matID; const ElementBase *elref = theElements[mpmptr->ElemID()]; // element containing this particle int numnds=elref->NumberNodes(); double rvalue; for(int i=1;i<=numnds;i++) { int mi=elref->nodes[i-1]; // 1 based node // look for setting function in one to three directions // GetVectorSetting() returns true if function has set the velocity, otherwise it return FALSE bool hasDir[3]; Vector rvel; if(rigid->GetVectorSetting(&rvel,hasDir,mtime,&mpmptr->pos)) { // velocity set by 1 to 3 functions as determined by hasDir[i] if(hasDir[0]) { mpmptr->vel.x = rvel.x; SetRigidBCs(mi,matid0,X_DIRECTION,rvel.x,0.,rigid->mirrored, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } if(hasDir[1]) { mpmptr->vel.y = rvel.y; SetRigidBCs(mi,matid0,Y_DIRECTION,rvel.y,0.,rigid->mirrored, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } if(hasDir[2]) { mpmptr->vel.z = rvel.z; SetRigidBCs(mi,matid0,Z_DIRECTION,rvel.z,0.,rigid->mirrored, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } } else { // velocity set by particle velocity in selected directions if(rigid->RigidDirection(X_DIRECTION)) { SetRigidBCs(mi,matid0,X_DIRECTION,mpmptr->vel.x,0.,rigid->mirrored, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } if(rigid->RigidDirection(Y_DIRECTION)) { SetRigidBCs(mi,matid0,Y_DIRECTION,mpmptr->vel.y,0.,rigid->mirrored, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } if(rigid->RigidDirection(Z_DIRECTION)) { SetRigidBCs(mi,matid0,Z_DIRECTION,mpmptr->vel.z,0.,rigid->mirrored, (BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC, (BoundaryCondition **)&firstRigidVelocityBC,(BoundaryCondition **)&reuseRigidVelocityBC); } } // temperature if(rigid->RigidTemperature()) { if(rigid->GetValueSetting(&rvalue,mtime,&mpmptr->pos)) mpmptr->pTemperature=rvalue; SetRigidBCs(mi,matid0,TEMP_DIRECTION,mpmptr->pTemperature,0.,0, (BoundaryCondition **)&firstTempBC,(BoundaryCondition **)&lastTempBC, (BoundaryCondition **)&firstRigidTempBC,(BoundaryCondition **)&reuseRigidTempBC); } // concentration if(rigid->RigidConcentration()) { if(rigid->GetValueSetting(&rvalue,mtime,&mpmptr->pos)) mpmptr->pConcentration=rvalue; SetRigidBCs(mi,matid0,CONC_DIRECTION,mpmptr->pConcentration,0.,0, (BoundaryCondition **)&firstConcBC,(BoundaryCondition **)&lastConcBC, (BoundaryCondition **)&firstRigidConcBC,(BoundaryCondition **)&reuseRigidConcBC); } } } // if any left over rigid BCs, delete them now RemoveRigidBCs((BoundaryCondition **)&firstVelocityBC,(BoundaryCondition **)&lastVelocityBC,(BoundaryCondition **)&firstRigidVelocityBC); RemoveRigidBCs((BoundaryCondition **)&firstTempBC,(BoundaryCondition **)&lastTempBC,(BoundaryCondition **)&firstRigidTempBC); RemoveRigidBCs((BoundaryCondition **)&firstConcBC,(BoundaryCondition **)&lastConcBC,(BoundaryCondition **)&firstRigidConcBC); #ifdef COMBINE_RIGID_MATERIALS bool combineRigid = firstCrack!=NULL && fmobj->multiMaterialMode && fmobj->hasRigidContactParticles; #endif #pragma mark ... POST EXTRAPOLATION TASKS // Post mass and momentum extrapolation calculations on nodes #pragma omp parallel { // variables for each thread CrackNode *firstCrackNode=NULL,*lastCrackNode=NULL; MaterialInterfaceNode *firstInterfaceNode=NULL,*lastInterfaceNode=NULL; // Each pass in this loop should be independent #pragma omp for nowait for(int i=1;i<=nnodes;i++) { // node reference NodalPoint *ndptr = nd[i]; try { #ifdef COMBINE_RIGID_MATERIALS // combine rigid fields if necessary if(combineRigid) ndptr->CopyRigidParticleField(); #endif // Get total nodal masses and count materials if multimaterial mode ndptr->CalcTotalMassAndCount(); // multimaterial contact if(fmobj->multiMaterialMode) ndptr->MaterialContactOnNode(timestep,MASS_MOMENTUM_CALL,&firstInterfaceNode,&lastInterfaceNode); // crack contact if(firstCrack!=NULL) ndptr->CrackContact(FALSE,0.,&firstCrackNode,&lastCrackNode); // get transport values on nodes TransportTask *nextTransport=transportTasks; while(nextTransport!=NULL) nextTransport = nextTransport->GetNodalValue(ndptr); } catch(CommonException err) { if(massErr==NULL) { #pragma omp critical massErr = new CommonException(err); } } } #pragma omp critical { // link up crack nodes if(lastCrackNode != NULL) { if(CrackNode::currentCNode != NULL) firstCrackNode->SetPrevBC(CrackNode::currentCNode); CrackNode::currentCNode = lastCrackNode; } // link up interface nodes if(lastInterfaceNode != NULL) { if(MaterialInterfaceNode::currentIntNode != NULL) firstInterfaceNode->SetPrevBC(MaterialInterfaceNode::currentIntNode); MaterialInterfaceNode::currentIntNode = lastInterfaceNode; } } } // throw any errors if(massErr!=NULL) throw *massErr; #pragma mark ... IMPOSE BOUNDARY CONDITIONS // Impose transport BCs and extrapolate gradients to the particles TransportTask *nextTransport=transportTasks; while(nextTransport!=NULL) { nextTransport->ImposeValueBCs(mtime); nextTransport = nextTransport->GetGradients(mtime); } // locate BCs with reflected nodes if(firstRigidVelocityBC!=NULL) { NodalVelBC *nextBC=firstRigidVelocityBC; double mstime=1000.*mtime; //cout << "# Find Reflected Nodes" << endl; while(nextBC!=NULL) nextBC = nextBC->SetMirroredVelBC(mstime); } // used to call class methods for material contact and crack contact here // Impose velocity BCs NodalVelBC::GridMomentumConditions(TRUE); }
// Get total grid point forces (except external forces) // throws CommonException() void GridForcesTask::Execute(void) { CommonException *forceErr = NULL; // need to be private in threads #ifdef CONST_ARRAYS double fn[MAX_SHAPE_NODES],xDeriv[MAX_SHAPE_NODES],yDeriv[MAX_SHAPE_NODES],zDeriv[MAX_SHAPE_NODES]; int ndsArray[MAX_SHAPE_NODES]; #else double fn[maxShapeNodes],xDeriv[maxShapeNodes],yDeriv[maxShapeNodes],zDeriv[maxShapeNodes]; int ndsArray[maxShapeNodes]; #endif // loop over non-rigid particles - this parallel part changes only particle p // forces are stored on ghost nodes, which are sent to real nodes in next non-parallel loop #pragma omp parallel private(ndsArray,fn,xDeriv,yDeriv,zDeriv) { // in case 2D planar for(int i=0;i<maxShapeNodes;i++) zDeriv[i] = 0.; // patch for this thread int pn = GetPatchNumber(); try { MPMBase *mpmptr = patches[pn]->GetFirstBlockPointer(FIRST_NONRIGID); while(mpmptr!=NULL) { const MaterialBase *matref = theMaterials[mpmptr->MatID()]; // material class (read only) int matfld = matref->GetField(); // get transport tensors (if needed) TransportProperties t; if(transportTasks!=NULL) matref->GetTransportProps(mpmptr,fmobj->np,&t); // find shape functions and derviatives const ElementBase *elemref = theElements[mpmptr->ElemID()]; int *nds = ndsArray; elemref->GetShapeGradients(fn,&nds,xDeriv,yDeriv,zDeriv,mpmptr); int numnds = nds[0]; // Add particle property to buffer on the material point (needed to allow parallel code) short vfld; NodalPoint *ndptr; for(int i=1;i<=numnds;i++) { vfld = (short)mpmptr->vfld[i]; // crack velocity field to use // total force vector = internal + external forces // (in g mm/sec^2 or micro N) Vector theFrc; mpmptr->GetFintPlusFext(&theFrc,fn[i],xDeriv[i],yDeriv[i],zDeriv[i]); // add body forces (do in outside loop now) // add the total force to nodal point ndptr = GetNodePointer(pn,nds[i]); ndptr->AddFtotTask3(vfld,matfld,&theFrc); #ifdef CHECK_NAN if(theFrc.x!=theFrc.x || theFrc.y!=theFrc.y || theFrc.z!=theFrc.z) { #pragma omp critical (output) { cout << "\n# GridForcesTask::Execute: bad nodal force vfld = " << vfld << ", matfld = " << matfld; PrintVector(" theFrc = ",&theFrc); cout << endl; ndptr->Describe(); } } #endif // transport forces TransportTask *nextTransport=transportTasks; while(nextTransport!=NULL) nextTransport=nextTransport->AddForces(ndptr,mpmptr,fn[i],xDeriv[i],yDeriv[i],zDeriv[i],&t); } // next material point mpmptr = (MPMBase *)mpmptr->GetNextObject(); } } catch(CommonException& err) { if(forceErr==NULL) { #pragma omp critical (error) forceErr = new CommonException(err); } } catch(std::bad_alloc&) { if(forceErr==NULL) { #pragma omp critical (error) forceErr = new CommonException("Memory error","GridForcesTask::Execute"); } } catch(...) { if(forceErr==NULL) { #pragma omp critical (error) forceErr = new CommonException("Unexpected error","GridForcesTask::Execute"); } } } // throw errors now if(forceErr!=NULL) throw *forceErr; // reduction of ghost node forces to real nodes int totalPatches = fmobj->GetTotalNumberOfPatches(); if(totalPatches>1) { for(int pn=0;pn<totalPatches;pn++) patches[pn]->GridForcesReduction(); } }
// Get total grid point forces (except external forces) void UpdateStrainsLastContactTask::Execute(void) { CommonException *uslErr = NULL; int nds[maxShapeNodes]; double fn[maxShapeNodes],xDeriv[maxShapeNodes],yDeriv[maxShapeNodes],zDeriv[maxShapeNodes]; #pragma omp parallel private(nds,fn,xDeriv,yDeriv,zDeriv) { // in case 2D planar for(int i=0;i<maxShapeNodes;i++) zDeriv[i] = 0.; try { #pragma omp for // zero again (which finds new positions for contact rigid particle data on the nodes) for(int i=1;i<=nnodes;i++) nd[i]->RezeroNodeTask6(timestep); // zero ghost nodes on this patch int pn = GetPatchNumber(); patches[pn]->RezeroNodeTask6(timestep); // loop over non-rigid particles only - this parallel part changes only particle p // mass, momenta, etc are stored on ghost nodes, which are sent to real nodes in next non-parallel loop MPMBase *mpmptr = patches[pn]->GetFirstBlockPointer(FIRST_NONRIGID); while(mpmptr!=NULL) { const MaterialBase *matref = theMaterials[mpmptr->MatID()]; int matfld = matref->GetField(); // find shape functions (why ever need gradients?) const ElementBase *elref = theElements[mpmptr->ElemID()]; int numnds; if(fmobj->multiMaterialMode) { // Need gradients for volume gradient elref->GetShapeGradients(&numnds,fn,nds,xDeriv,yDeriv,zDeriv,mpmptr); } else elref->GetShapeFunctions(&numnds,fn,nds,mpmptr); short vfld; NodalPoint *ndptr; for(int i=1;i<=numnds;i++) { // get node pointer ndptr = GetNodePointer(pn,nds[i]); // add mass and momentum this task vfld = (short)mpmptr->vfld[i]; ndptr->AddMassMomentumLast(mpmptr,vfld,matfld,fn[i],xDeriv[i],yDeriv[i],zDeriv[i]); } // next non-rigid material point mpmptr = (MPMBase *)mpmptr->GetNextObject(); } } catch(CommonException err) { if(uslErr==NULL) { #pragma omp critical uslErr = new CommonException(err); } } } // throw errors now if(uslErr!=NULL) throw *uslErr; // reduction of ghost node forces to real nodes int totalPatches = fmobj->GetTotalNumberOfPatches(); if(totalPatches>1) { for(int pn=0;pn<totalPatches;pn++) patches[pn]->MassAndMomentumReductionLast(); } // grid temperature is never updated unless needed here // update nodal values for transport properties (when coupled to strain) TransportTask *nextTransport=transportTasks; while(nextTransport!=NULL) nextTransport=nextTransport->UpdateNodalValues(timestep); // adjust momenta for multimaterial contact if(fmobj->multiMaterialMode) { for(int i=1;i<=nnodes;i++) nd[i]->MaterialContactOnNode(timestep,UPDATE_STRAINS_LAST_CALL,NULL,NULL); } // adjust momenta for crack contact if(firstCrack!=NULL) CrackNode::ContactOnKnownNodes(); // impose grid boundary conditions NodalVelBC::GridMomentumConditions(FALSE); // update strains based on current velocities UpdateStrainsFirstTask::FullStrainUpdate(strainTimestep,(fmobj->mpmApproach==USAVG_METHOD),fmobj->np); }
// Update particle position, velocity, temp, and conc void UpdateParticlesTask::Execute(void) { CommonException *upErr = NULL; int numnds,nds[maxShapeNodes]; double fn[maxShapeNodes]; Vector vgpnp1; // Damping terms on the grid or on the particles // particleAlpha = alpha(PIC)/dt + pdamping(t) // gridAlpha = -alpha(PIC)/dt + damping(t) double particleAlpha = bodyFrc.GetParticleDamping(mtime); double gridAlpha = bodyFrc.GetDamping(mtime); // copy to local values (OSParticulas uses to implement material damping) double localParticleAlpha = particleAlpha; double localGridAlpha = gridAlpha; // Update particle position, velocity, temp, and conc #pragma omp parallel for private(numnds,nds,fn,vgpnp1) for(int p=0;p<nmpmsNR;p++) { MPMBase *mpmptr = mpm[p]; try { // get shape functions const ElementBase *elemRef = theElements[mpmptr->ElemID()]; elemRef->GetShapeFunctions(&numnds,fn,nds,mpmptr); // Update particle position and velocity const MaterialBase *matRef=theMaterials[mpmptr->MatID()]; int matfld=matRef->GetField(); Vector *acc=mpmptr->GetAcc(); ZeroVector(acc); ZeroVector(&vgpnp1); double rate[2]; // only two possible transport tasks rate[0] = rate[1] = 0.; int task; TransportTask *nextTransport; short vfld; // Loop over nodes for(int i=1;i<=numnds;i++) { // increment velocity and acceleraton const NodalPoint *ndptr = nd[nds[i]]; vfld = (short)mpmptr->vfld[i]; ndptr->IncrementDelvaTask5(vfld,matfld,fn[i],&vgpnp1,acc); #ifdef CHECK_NAN if(vgpnp1.x!=vgpnp1.x || vgpnp1.y!=vgpnp1.y || vgpnp1.z!=vgpnp1.z) { cout << "\n# UpdateParticlesTask::Execute: bad material velocity field for vfld = " << vfld << endl; ndptr->Describe(); } #endif // increment transport rates nextTransport=transportTasks; task=0; while(nextTransport!=NULL) nextTransport=nextTransport->IncrementTransportRate(ndptr,fn[i],rate[task++]); } // Find vgpn Vector vgpn = vgpnp1; AddScaledVector(&vgpn,acc,-timestep); // find effective grid acceleration and velocity AddScaledVector(acc,&vgpn,-localGridAlpha); AddScaledVector(&vgpnp1,&vgpn,-timestep*localGridAlpha); // update position, and must be before velocity update because updates need initial velocity // This section does second order update mpmptr->MovePosition(timestep,&vgpnp1,0.5*timestep,localParticleAlpha); // update velocity in mm/sec mpmptr->MoveVelocity(timestep,localParticleAlpha); // update transport values nextTransport=transportTasks; task=0; while(nextTransport!=NULL) nextTransport=nextTransport->MoveTransportValue(mpmptr,timestep,rate[task++]); // thermal ramp thermal.UpdateParticleTemperature(&mpmptr->pTemperature,timestep); // energy coupling here if conduction not doing it if(!ConductionTask::active) { if(ConductionTask::adiabatic) { double energy = mpmptr->GetDispEnergy(); // in nJ/g double Cv = matRef->GetHeatCapacity(mpmptr); // in nJ/(g-K) mpmptr->pTemperature += energy/Cv; // in K } mpmptr->SetDispEnergy(0.); } } catch(CommonException err) { if(upErr==NULL) { #pragma omp critical upErr = new CommonException(err); } } } // throw any errors if(upErr!=NULL) throw *upErr; // rigid materials move at their current velocity for(int p=nmpmsNR;p<nmpms;p++) { mpm[p]->MovePosition(timestep,&mpm[p]->vel,0.,0.); } }
// Get mass matrix, find dimensionless particle locations, // and find grid momenta void MassAndMomentumTask::Execute(void) { CommonException *massErr = NULL; double fn[maxShapeNodes],xDeriv[maxShapeNodes],yDeriv[maxShapeNodes],zDeriv[maxShapeNodes]; int nds[maxShapeNodes]; #pragma mark ... UPDATE RIGID CONTACT PARTICLES // Set rigid BC contact material velocities first (so loop can be parallel) // GetVectorSetting() uses globals and therefore can't be parallel if(nmpmsRC>nmpmsNR) { Vector newvel; bool hasDir[3]; for(int p=nmpmsNR;p<nmpmsRC;p++) { MPMBase *mpmptr = mpm[p]; const RigidMaterial *matID = (RigidMaterial *)theMaterials[mpm[p]->MatID()]; if(matID->GetVectorSetting(&newvel,hasDir,mtime,&mpmptr->pos)) { // change velocity if functions being used, otherwise keep velocity constant if(hasDir[0]) mpmptr->vel.x = newvel.x; if(hasDir[1]) mpmptr->vel.y = newvel.y; if(hasDir[2]) mpmptr->vel.z = newvel.z; } } } // loop over non-rigid and rigid contact particles - this parallel part changes only particle p // mass, momenta, etc are stored on ghost nodes, which are sent to real nodes in next non-parallel loop //for(int pn=0;pn<4;pn++) #pragma omp parallel private(fn,xDeriv,yDeriv,zDeriv,nds) { // thread for patch pn int pn = GetPatchNumber(); // in case 2D planar for(int i=0;i<maxShapeNodes;i++) zDeriv[i] = 0.; #pragma mark ... EXTRAPOLATE NONRIGID AND RIGID CONTACT PARTICLES try { for(int block=FIRST_NONRIGID;block<=FIRST_RIGID_CONTACT;block++) { MPMBase *mpmptr = patches[pn]->GetFirstBlockPointer(block); while(mpmptr!=NULL) { const MaterialBase *matID = theMaterials[mpmptr->MatID()]; // material object for this particle int matfld = matID->GetField(); // material velocity field // get nodes and shape function for material point p int i,numnds; const ElementBase *elref = theElements[mpmptr->ElemID()]; // element containing this particle if(fmobj->multiMaterialMode) elref->GetShapeGradients(&numnds,fn,nds,xDeriv,yDeriv,zDeriv,mpmptr); else elref->GetShapeFunctions(&numnds,fn,nds,mpmptr); // Add particle property to each node in the element short vfld; NodalPoint *ndptr; for(i=1;i<=numnds;i++) { // get node pointer ndptr = GetNodePointer(pn,nds[i]); // add mass and momentum (and maybe contact stuff) to this node vfld = mpmptr->vfld[i]; ndptr->AddMassMomentum(mpmptr,vfld,matfld,fn[i],xDeriv[i],yDeriv[i],zDeriv[i], 1,block==FIRST_NONRIGID); } // next material point mpmptr = (MPMBase *)mpmptr->GetNextObject(); } } } catch(CommonException err) { if(massErr==NULL) { #pragma omp critical massErr = new CommonException(err); } } catch(...) { cout << "Unknown exception in MassAndMomentumTask()" << endl; } } // throw now - only possible error if too many CPDI nodes in 3D if(massErr!=NULL) throw *massErr; // reduction of ghost node forces to real nodes int totalPatches = fmobj->GetTotalNumberOfPatches(); if(totalPatches>1) { for(int pn=0;pn<totalPatches;pn++) patches[pn]->MassAndMomentumReduction(); } }