/* This is called after PreliminaryCalcs() and just before first MPM time step and it is only called if the material is actually in use by one or more particles If material cannot be used in current analysis type throw an exception Subclass that overrides must pass on to super class */ void MaterialBase::ValidateForUse(int np) const { int i; for(i=0;i<=1;i++) { if(i==1 && criterion[i]==NO_PROPAGATION) break; // skip if no alternate criterion if(tractionMat[i]>0) { if(tractionMat[i]>nmat) { throw CommonException("Material with undefined traction law material for propagation", "MaterialBase::ValidateForUse"); } if(!theMaterials[tractionMat[i]-1]->isTractionLaw()) { throw CommonException("Material with propagation material that is not a traction law", "MaterialBase::ValidateForUse"); } } } // check for unsupported alternate propagation criterion if(criterion[1]==STEADYSTATEGROWTH) { throw CommonException("The alternate propagation criterion cannot be steady state crack growth.", "MaterialBase::ValidateForUse"); } if(ConductionTask::active || ConductionTask::adiabatic) { if(heatCapacity<=0. && !Rigid()) { throw CommonException("Thermal conduction and/or mechanical energy cannot be done using materials that have zero heat capacity.", "MaterialBase::ValidateForUse"); } } }
// called once at start of MPM analysis - initialize and print info CustomTask *CarnotCycle::Initialize(void) { cout << "Carnot Cyle Simulation." << endl; // time interval cout << " Step 1: Isothermal expansion to " << V1rel << "*V0" << endl; cout << " Step 2: Adibatic expansion to temperature " << T2 << endl; if(V3rel>0.) cout << " Step 3: Isothermal compression to " << V3rel << "*V0" << endl; else cout << " Step 3: Isothermal compression to V3/V0 = V2/V1" << endl; cout << " Step 2: Adibatic compression to temperature " << thermal.reference << endl; if(V1rel<1.) throw CommonException("The first expansion volume (V1rel) must be greater than 1","CarnotCycle::Initialize()"); if(T2>thermal.reference || T2<0) throw CommonException("The second temperature, T2, must be less than the reference temperature.","CarnotCycle::Initialize()"); if(V3rel>0. and V3rel<1.) throw CommonException("The thirds expansion volume (V3rel) must be greater than 1","CarnotCycle::Initialize()"); // initial settings carnotStep = 0; ConductionTask::adiabatic = FALSE; MaterialBase::isolatedSystemAndParticles = FALSE; return nextTask; }
// called once at start of MPM analysis - initialize and print info CustomTask *VTKArchive::Initialize(void) { cout << "Archive grid results to VTK files." << endl; if(!mpmgrid.IsStructuredGrid()) throw CommonException("VTKArchive task requires use of a generated grid","VTKArchive::Initialize"); // time interval cout << " Archive time: "; if(customArchiveTime>0) { cout << customArchiveTime << " ms"; customArchiveTime/=1000.; // convert to sec if(nextCustomArchiveTime<0.) { nextCustomArchiveTime=0.0; cout << endl; } else { cout << ", starting at " << nextCustomArchiveTime << " ms" << endl; nextCustomArchiveTime/=1000.; // convert to sec } } else cout << "same as particle archives" << endl; // quantities unsigned int q; cout << " Archiving: " ; int len=14; for(q=0;q<quantity.size();q++) { char *name=quantityName[q]; if(len+strlen(name)>70) { cout << "\n "; len=3; } cout << name; if(quantity[q]==VTK_VOLUMEGRADIENT) { if(qparam[q]<0) { cout << endl; throw CommonException("VTKArchive volumegradient must be set to an available material","VTKArchive::Initialize"); } int matnum = intArgs[qparam[q]]; cout << " (material #" << matnum << ")"; len+=14; // this is for matnum<10, but exact length does not matter if(matnum<1 || matnum>nmat) { cout << endl; throw CommonException("VTKArchive volumegradient must be for an available material","VTKArchive::Initialize"); } qparam[q] = matnum; } if(q<quantity.size()-1) cout << ", "; len+=strlen(name)+2; } cout << endl; return nextTask; }
// if analysis not allowed, throw an exception void HEMGEOSMaterial::ValidateForUse(int np) const { if(thermal.reference<=0) { throw CommonException("MGEOSMaterial material requires the simulation to set the stress free temperature in degrees K", "MGSCGLMaterial::ValidateForUse"); } if(np==PLANE_STRESS_MPM) { throw CommonException("MGEOSMaterial material has not yet been updated to do plane stress calculations", "MGSCGLMaterial::ValidateForUse"); } // call super class HEIsotropic::ValidateForUse(np); }
// Print contact law settings for cracks and finalize variables void CrackSurfaceContact::Output(void) { // allocate memory for custom crack contact laws char *p=new char[(numberOfCracks+1)*sizeof(ContactLaw *)]; crackContactLaw=(ContactLaw **)p; // Global material contact law (must be set,if not force to frictionless) crackContactLawID = MaterialBase::GetContactLawNum(crackContactLawID); if(crackContactLawID<0) throw CommonException("Crack settings must select a default contact law","CrackSurfaceContact::Output"); crackContactLaw[0] = (ContactLaw *)theMaterials[crackContactLawID]; cout << "Default Contact Law: " << crackContactLaw[0]->name << " (number " << (crackContactLawID+1) << ")" << endl; if(crackContactLaw[0]->IsImperfectInterface()) hasImperfectInterface=true; // print other settings cout << "Contact Detection: Normal cod < 0 AND normal dv < 0" << endl; mpmgrid.OutputContactByDisplacements(); 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; }
// check if analysis is allowed // throws CommonException() void TaitLiquid::ValidateForUse(int np) const { if(np==PLANE_STRESS_MPM) { throw CommonException("TaitLiquid material cannot do 2D plane stress analysis", "TaitLiquid::ValidateForUse"); } return HyperElastic::ValidateForUse(np); }
void ActorList::addActor(Actor::ptr p_Actor) { if (m_Actors.find(p_Actor->getId()) != m_Actors.end()) { throw CommonException("You may not add an already existing actor", __LINE__, __FILE__); } m_Actors[p_Actor->getId()] = p_Actor; }
// plane stress not allowed in viscoelasticity void Viscoelastic::ValidateForUse(int np) const { if(np==PLANE_STRESS_MPM) { throw CommonException("Viscoelastic materials are not allowed in plane stress calculations", "Viscoelastic::ValidateForUse"); } //call super class (why can't call super class?) MaterialBase::ValidateForUse(np); }
// plane stress not allowed in viscoelasticity // throws CommonException() void HEIsotropic::ValidateForUse(int np) const { if(np==PLANE_STRESS_MPM) { throw CommonException("HEIsotropic materials cannot be used in plane stress MPM yet", "HEIsotropic::ValidateForUse"); } //call super class (why can't call super class?) HyperElastic::ValidateForUse(np); }
// plane stress not allowed in viscoelasticity // throws CommonException() void ClampedNeohookean::ValidateForUse(int np) const { if(np==PLANE_STRESS_MPM) { throw CommonException("Clamped Neohookean material cannot be used in plane stress MPM yet", "ClampedNeohookean::ValidateForUse"); } //call super class (why can't call super class?) Neohookean::ValidateForUse(np); }
// 3D not allowed void BistableIsotropic::ValidateForUse(int np) const { if(np==THREED_MPM || np==AXISYMMETRIC_MPM) { throw CommonException("BistableIsotropic materials cannot do 3D or Axisymmetric MPM analysis", "BistableIsotropic::ValidateForUse"); } // call super class (why can't call super class?) IsotropicMat::ValidateForUse(np); }
// Print contact law settings for cracks and finalize variables void CrackSurfaceContact::MaterialOutput(void) { // Global material contact law (must be set,if not force to frictionless) materialContactLawID = MaterialBase::GetContactLawNum(materialContactLawID); if(materialContactLawID<0) throw CommonException("Multimaterial mode must select a default contact law","CrackSurfaceContact::MaterialOutput"); materialContactLaw = (ContactLaw *)theMaterials[materialContactLawID]; cout << "Default Contact Law: " << materialContactLaw->name << " (number " << (materialContactLawID+1) << ")" << endl; if(materialContactLaw->IsImperfectInterface()) hasImperfectInterface=true; // print contact detection method char join[3]; join[0]=0; 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); default: break; } cout << endl; }
// report a file error to some file void ArchiveData::FileError(const char *msg,const char *filename,const char *method) { char errNo[50]; sprintf(errNo,"%d",errno); char *errMsg=new char[strlen(msg)+strlen(filename)+strlen(errNo)+15]; strcpy(errMsg,msg); strcat(errMsg," (file: "); strcat(errMsg,filename); strcat(errMsg,", #"); strcat(errMsg,errNo); strcat(errMsg,")."); throw CommonException(errMsg,method); }
SpellInstance::ptr SpellFactory::createSpellInstance(const std::string& p_Spell, Vector3 p_Direction) { auto spellDef = m_SpellDefinitionMap.find(p_Spell); if (spellDef == m_SpellDefinitionMap.end()) { throw CommonException("Can not create spell from a missing spell definition (" + p_Spell + ")", __LINE__, __FILE__); } SpellInstance::ptr instance(new SpellInstance); instance->init(spellDef->second, p_Direction); return instance; }
// Create global archive file void ArchiveData::CreateGlobalFile(void) { FILE *fp; char fline[1000]; GlobalQuantity *nextGlobal; // skip if none, but error if tried to define global quantities if(globalTime<0.) { globalTime=-1.; if(firstGlobal!=NULL) throw CommonException("<GlobalArchive> was used but never activated with a <GlobalArchiveTime> command.","ArchiveData::CreateGlobalFile"); return; } // get relative path name to the file globalFile=new char[strlen(outputDir)+strlen(archiveRoot)+8]; sprintf(globalFile,"%s%s.global",outputDir,archiveRoot); // create and open the file if((fp=fopen(globalFile,"w"))==NULL) goto abort; // write color strcpy(fline,"#setColor"); nextGlobal=firstGlobal; while(nextGlobal!=NULL) nextGlobal=nextGlobal->AppendColor(fline); strcat(fline,"\n"); if(fwrite(fline,strlen(fline),1,fp)!=1) goto abort; // write name strcpy(fline,"#setName"); nextGlobal=firstGlobal; while(nextGlobal!=NULL) nextGlobal=nextGlobal->AppendName(fline); strcat(fline,"\n"); if(fwrite(fline,strlen(fline),1,fp)!=1) goto abort; // close the file if(fclose(fp)!=0) goto abort; // section in output file PrintSection("ARCHIVED GLOBAL RESULTS"); cout << "Global data file: " << archiveRoot << ".global" << endl; cout << endl; return; abort: FileError("Global archive file creation failed",globalFile,"ArchiveData::CreateGlobalFile"); }
// Print contact law settings (if has one) and finalize crack law and set if has imperfect interface void CrackSurfaceContact::CustomCrackContactOutput(int &customCrackID,int number) { // no custom law was set if(customCrackID<0) { crackContactLaw[number] = crackContactLaw[0]; return; } // custom law customCrackID = MaterialBase::GetContactLawNum(customCrackID); if(customCrackID<0) throw CommonException("Custom crack contact must select a default contact law","CrackSurfaceContact::Output"); crackContactLaw[number] = (ContactLaw *)theMaterials[customCrackID]; cout << " Custom Contact Law: " << crackContactLaw[number]->name << " (number " << (customCrackID+1) << ")" << endl; if(crackContactLaw[number]->IsImperfectInterface()) hasImperfectInterface=true; }
// on start up initialize number of CPDI nodes (if needed) // done here in case need more initializations in the future // throws CommonException() if CPDI type is not allowed void ElementBase::InitializeCPDI(bool isThreeD) { if(isThreeD) { if(useGimp == LINEAR_CPDI) { numCPDINodes = 8; } else if(useGimp == QUADRATIC_CPDI) { throw CommonException("qCPDI is not yet implemented for 3D (use lCPDI instead).","ElementBase::InitializeCPDI"); } } else { if(useGimp == LINEAR_CPDI || useGimp==LINEAR_CPDI_AS) { numCPDINodes = 4; } else if(useGimp == QUADRATIC_CPDI) { numCPDINodes = 9; } } }
// when set, return total number of materials if this is a new one, or 1 if not in multimaterial mode // not thread safe due to push_back() int MaterialBase::SetField(int fieldNum,bool multiMaterials,int matid,int &activeNum) { if(!multiMaterials) { if(field<0) { field=0; activeField=activeNum; fieldNum=1; activeNum++; activeMatIDs.push_back(matid); int altBuffer,matBuffer = SizeOfMechanicalProperties(altBuffer); if(matBuffer > maxPropertyBufferSize) maxPropertyBufferSize = matBuffer; if(altBuffer > maxAltBufferSize) maxAltBufferSize = altBuffer; } } else { if(field<0) { // if sharing a field, look up shared material. if(shareMatField>0) { if(shareMatField>nmat) { throw CommonException("Material class trying to share velocity field with an undefined material type", "MaterialBase::SetField"); } // must match for rigid feature MaterialBase *matRef = theMaterials[shareMatField-1]; if(matRef->Rigid() != Rigid()) { throw CommonException("Material class trying to share velocity field with an incompatible material type", "MaterialBase::SetField"); } // base material cannot share too if(matRef->GetShareMatField()>=0) { throw CommonException("Material class trying to share velocity field with a material that share's its field", "MaterialBase::SetField"); } // set field to other material (and set other material if needed field = matRef->GetField(); if(field<0) { fieldNum = matRef->SetField(fieldNum,multiMaterials,shareMatField-1,activeNum); field = fieldNum-1; } } else { field=fieldNum; fieldNum++; // fieldMatIDs[i] for i=0 to # materials is material index for that material velocity field // when materials share fields, it points to the based shared material fieldMatIDs.push_back(matid); } // for first particle using this material, add to active material IDs and check required // material buffer sizes if(activeNum>=0) { activeField=activeNum; activeNum++; activeMatIDs.push_back(matid); int altBuffer,matBuffer = SizeOfMechanicalProperties(altBuffer); if(matBuffer > maxPropertyBufferSize) maxPropertyBufferSize = matBuffer; if(altBuffer > maxAltBufferSize) maxAltBufferSize = altBuffer; } } } return fieldNum; }
// Called when custom tasks are all done on a step // throw CommonException() to end simulation CustomTask *ReverseLoad::FinishForStep(void) { // change to true trigger or on end of holding phase bool status = false; // after checking decide if stop or if final hold phase switch(reversed) { case REVERSED_PHASE: // once reversed, check if reached final time and then exit // othersize just continue if(style==REVERSE && mtime>finalTime) throw CommonException("Load or displacement has returned to zero","ReverseLoad::FinishForStep"); break; case HOLDING_PHASE: // change status to true if done with holding phase if(mtime>endHoldTime) status=true; break; case CHECKING_PHASE: default: // check global quantity if(quantity>=0) { // see if has passed desider value status = archiver->PassedLastArchived(quantity,finalLength); } // check cracks else if(propagateTask->theResult!=NOGROWTH) { // check desired crack or all cracks int cnum=0; CrackHeader *nextCrack=firstCrack; while(nextCrack!=NULL) { cnum++; if(crackNum==0 || crackNum==cnum) { // found crack, check the length if(nextCrack->Length()>finalLength) status = true; // exit if critical or only one crack to check if(status || crackNum==cnum) break; } nextCrack=(CrackHeader *)nextCrack->GetNextObject(); } } break; } // if status is false, then nothing to do if(!status) return nextTask; // Print message that task has been triggered if(reversed == CHECKING_PHASE) { if(quantity>=0) { if(style==ABORT && holdTime<0.) throw CommonException("Global quantity has reached specified value","ReverseLoad::FinishForStep"); cout << "# Critical global quantity reached at time t: " << mtime*UnitsController::Scaling(1.e3) << " " << UnitsController::Label(ALTTIME_UNITS) << endl; } else { if(style==ABORT && holdTime<0.) throw CommonException("Crack has reached specified length","ReverseLoad::FinishForStep"); // stop propgation propagateTask->ArrestGrowth(TRUE); cout << "# Crack growth arrested at time t: " << mtime*UnitsController::Scaling(1.e3) << " " << UnitsController::Label(ALTTIME_UNITS) << endl; } // final time if reversing finalTime = 2.*mtime; // if holding, wait to trigger change until latter if(holdTime>0.) { reversed = HOLDING_PHASE; endHoldTime = mtime + holdTime; finalTime = 2.*mtime + holdTime; } else { reversed = REVERSED_PHASE ; finalTime = 2.*mtime; } } else { // if an abort after holding, exit now if(style==ABORT) throw CommonException("ReverseLoad task hold time has ended","ReverseLoad::FinishForStep"); // message that hold time is over cout << "# ReverseLoad task hold time ended at t:" << mtime*UnitsController::Scaling(1.e3) << " " << UnitsController::Label(ALTTIME_UNITS) << endl; reversed = REVERSED_PHASE; } // REVERSE: reverse linear loads, zero constant loads, reverse or zero constant velocity rigid particles // HOLD: make linear loads constant, stop constant velocity rigid particles // ABORT: here is holding, treat like a HOLD // finalTime will be twice current time, or if any reversed linear loads, the time last one gets to zero if(style!=NOCHANGE) { // load BCs MatPtLoadBC *nextLoad=firstLoadedPt; while(nextLoad!=NULL) { if(style==REVERSE) nextLoad=nextLoad->ReverseLinearLoad(mtime,&finalTime,reversed==HOLDING_PHASE); else nextLoad=nextLoad->MakeConstantLoad(mtime); } // traction BCs MatPtTractionBC *nextTraction=firstTractionPt; while(nextTraction!=NULL) { if(style==REVERSE) nextTraction=(MatPtTractionBC *)nextTraction->ReverseLinearLoad(mtime,&finalTime,reversed==HOLDING_PHASE); else nextTraction=(MatPtTractionBC *)nextTraction->MakeConstantLoad(mtime); } // reverse rigid particles at constant velocity int p; for(p=nmpmsNR;p<nmpms;p++) { RigidMaterial *mat = (RigidMaterial *)theMaterials[mpm[p]->MatID()]; if(mat->IsConstantVelocity()) { if(style==REVERSE) mpm[p]->ReverseParticle(reversed==HOLDING_PHASE,holdTime>0.); else mpm[p]->StopParticle(); } } } return nextTask; }
// return dimensionless location for material points void ElementBase::MPMPoints(short numPerElement,Vector *mpos) const { int i,j,k; double fxn[MaxElNd],row,zrow; if(NumberSides()==4) { switch(numPerElement) { case 4: // ENI or FNI - 2D only mpos[0].x=-.5; mpos[0].y=-.5; mpos[0].z=0.; mpos[1].x=.5; mpos[1].y=-.5; mpos[1].z=0.; mpos[2].x=-.5; mpos[2].y=.5; mpos[2].z=0.; mpos[3].x=.5; mpos[3].y=.5; mpos[3].z=0.; break; case 1: // CM of square or brick mpos[0].x=0.; mpos[0].y=0.; mpos[0].z=0.; break; case 8: // 3D box mpos[0].x=-.5; mpos[0].y=-.5; mpos[0].z=-.5; mpos[1].x=.5; mpos[1].y=-.5; mpos[1].z=-.5; mpos[2].x=-.5; mpos[2].y=.5; mpos[2].z=-.5; mpos[3].x=.5; mpos[3].y=.5; mpos[3].z=-.5; mpos[4].x=-.5; mpos[4].y=-.5; mpos[4].z=.5; mpos[5].x=.5; mpos[5].y=-.5; mpos[5].z=.5; mpos[6].x=-.5; mpos[6].y=.5; mpos[6].z=.5; mpos[7].x=.5; mpos[7].y=.5; mpos[7].z=.5; break; case 9: // 2D k=0; row = -2./3.; for(j=0;j<3;j++) { mpos[k].x=-2./3.; mpos[k].y=row; mpos[k].z=0.; mpos[k+1].x=0.; mpos[k+1].y=row; mpos[k+1].z=0.; mpos[k+2].x=2./3.; mpos[k+2].y=row; mpos[k+2].z=0.; k += 3; row += 2./3.; } break; case 16: // 2D k=0; row = -0.75; for(j=0;j<4;j++) { mpos[k].x=-0.75; mpos[k].y=row; mpos[k].z=0.; mpos[k+1].x=-.25; mpos[k+1].y=row; mpos[k+1].z=0.; mpos[k+2].x=.25; mpos[k+2].y=row; mpos[k+2].z=0.; mpos[k+3].x=.75; mpos[k+3].y=row; mpos[k+3].z=0.; k += 4; row += 0.5; } break; case 25: // 2D k=0; row = -0.8; for(j=0;j<5;j++) { mpos[k].x=-0.8; mpos[k].y=row; mpos[k].z=0.; mpos[k+1].x=-.4; mpos[k+1].y=row; mpos[k+1].z=0.; mpos[k+2].x=0.; mpos[k+2].y=row; mpos[k+2].z=0.; mpos[k+3].x=.4; mpos[k+3].y=row; mpos[k+3].z=0.; mpos[k+4].x=.8; mpos[k+4].y=row; mpos[k+4].z=0.; k += 5; row += 0.4; } break; case 27: // 3D k=0; zrow = -2./3.; for(i=0;i<3;i++) { row = -2./3.; for(j=0;j<3;j++) { mpos[k].x=-2./3.; mpos[k].y=row; mpos[k].z=zrow; mpos[k+1].x=0.; mpos[k+1].y=row; mpos[k+1].z=zrow; mpos[k+2].x=2./3.; mpos[k+2].y=row; mpos[k+2].z=zrow; k += 3; row += 2./3.; } zrow += 2./3.; } break; default: throw CommonException("Invalid number of material points per element.","ElementBase::MPMPoints"); break; } } // covert to x-y-z locations for(k=0;k<numPerElement;k++) { ShapeFunction(&mpos[k],FALSE,fxn,NULL,NULL,NULL); ZeroVector(&mpos[k]); for(j=0;j<NumberNodes();j++) { mpos[k].x+=nd[nodes[j]]->x*fxn[j]; mpos[k].y+=nd[nodes[j]]->y*fxn[j]; mpos[k].z+=nd[nodes[j]]->z*fxn[j]; } } }
void NairnFEA::FEAAnalysis() { char nline[200]; int result; int i; NodalDispBC *nextBC; double times[5]; #pragma mark --- TASK 0: INITIALIZE // start timer times[0]=CPUTime(); // get problem size and other initialization nsize=nnodes*nfree + numConstraints; nband=GetBandWidth(); if(np==AXI_SYM) { xax='r'; yax='z'; zax='t'; } // Stiffness matrix info PrintSection("TOTAL STIFFNESS MATRIX"); sprintf(nline,"Initial number of equations:%6d Initial bandwidth:%6d",nsize,nband); cout << nline << endl << endl; #pragma mark --- TASK 1: ALLOCATE R VECTOR // Allocate reaction vector and load with nodal loads and edge loads rm=(double *)malloc(sizeof(double)*(nsize+1)); if(rm==NULL) throw CommonException("Memory error allocating reaction vector (rm)","NairnFEA::FEAAnalysis"); for(i=1;i<=nsize;i++) rm[i]=0.; // add nodal loads to rm[] vector NodalLoad *nextLoad=firstLoadBC; while(nextLoad!=NULL) nextLoad=nextLoad->Reaction(rm,np,nfree); // add stresses on element edges to rm[] vector ForcesOnEdges(); #pragma mark --- TASK 2: GET STIFFNESS MATRIX // allocate and fill global stiffness matrix, st[][], and reaction vector, rm[] times[1]=CPUTime(); BuildStiffnessMatrix(); #pragma mark --- TASK 3: DISPLACEMENT BCs // Impose displacement boundary conditions and rotate // nodes for skew boundary conditions nextBC=firstDispBC; while(nextBC!=NULL) nextBC=nextBC->FixOrRotate(st,rm,nsize,nband,nfree); #pragma mark --- TASK 4: INVERT STIFFNESS MATRIX // Solve linear system for nodal displacements times[2]=CPUTime(); double *work=(double *)malloc(sizeof(double)*(nsize+1)); if(work==NULL) throw CommonException("Memory error allocating work vector for linear solver", "NairnFEA::FEAAnalysis"); result=gelbnd(st,nsize,nband,rm,work,0); if(result==1) { throw CommonException("Linear solver error: matrix is singular. Check boundary conditions.\n (Hint: turn on resequencing to check for mesh connectivity problem)", "NairnFEA::FEAAnalysis"); } else if(result==-1) { cout << "Linear solver warning: solution process was close to singular. Results might be invalid." << endl; } free(work); free(st); free(stiffnessMemory); #pragma mark --- TASK 5a: UNSKEW ROTATED NODES nextBC=firstDispBC; while(nextBC!=NULL) nextBC=nextBC->Unrotate(rm,nfree); #pragma mark --- TASK 6: OUTPUT RESULTS // time to here for performance evaluation double execTime=ElapsedTime(); // elpased time in secs times[3]=CPUTime(); // Print Displacements DisplacementResults(); // Calculate forces, stresses, and energy // print element forces and stresses ForceStressEnergyResults(); // Average nodal stresses AvgNodalStresses(); // reactivities at fixed nodes ReactionResults(); // strain energies EnergyResults(); // execution times times[4]=CPUTime(); PrintSection("EXECUTION TIMES AND MEMORY"); cout << "1. Allocate Memory: " << (times[1]-times[0]) << " secs" << endl; cout << "2. Build Stiffness Matrix: " << (times[2]-times[1]) << " secs" << endl; cout << "3. Solve Linear System: " << (times[3]-times[2]) << " secs" << endl; cout << "4. Write Results: " << (times[4]-times[3]) << " secs" << endl; cout << "5. Total Execution CPU Time: " << times[3] << " secs" << endl; cout << "6. Total Execution Elapsed Time: " << execTime << " secs" << endl; cout << "7. Scaling: " << times[3]/execTime << endl; //--------------------------------------------------- // Trailer cout << "\n***** NAIRNFEA RUN COMPLETED\n"; }
// Archive the results if it is time void ArchiveData::ArchiveResults(double atime) { double rho,rho0; double sxx,syy,sxy; char fname[500],fline[500]; int i,p; CrackHeader *nextCrack; // test global archiving GlobalArchive(atime); // see if desired if(atime<nextArchTime) return; nextArchTime+=archTime; // exit if using delayed archiving if(atime>0.9*timestep && atime<firstArchTime) return; // get relative path name to the file sprintf(fname,"%s%s.%d",outputDir,archiveRoot,fmobj->mstep); // output step number, time, and file name to results file for(i=strlen(fname);i>=0;i--) { if(fname[i]=='/') break; } sprintf(fline,"%7d %15.7e %s",fmobj->mstep,1000.*atime,&fname[i+1]); cout << fline << endl; // open the file ofstream afile; try { afile.open(fname, ios::out | ios::binary); if(!afile.is_open()) FileError("Cannot open an archive file",fname,"ArchiveData::ArchiveResults"); // write header created in SetArchiveHeader *timeStamp=1000.*atime; afile.write(archHeader,HEADER_LENGTH); if(afile.bad()) FileError("File error writing archive file header",fname,"ArchiveData::ArchiveResults"); } catch(CommonException err) { // give up on hopefully temporary file problem cout << "# " << err.Message() << endl; cout << "# Will try to continue" << endl; if(afile.is_open()) afile.close(); return; } // allocate space for one material point long blen=recSize; char *aptr=(char *)malloc(blen); if(aptr==NULL) throw CommonException("Out of memory allocating buffer for archive file","ArchiveData::ArchiveResults"); // all material points for(p=0;p<nmpms;p++) { // buffer is for one particle char *app=aptr; // must have these defaults // element ID *(int *)app=mpm[p]->ArchiveElemID(); app+=sizeof(int); // mass (g) *(double *)app=mpm[p]->mp; app+=sizeof(double); // material ID *(short *)app=mpm[p]->ArchiveMatID(); app+=sizeof(short); // fill in two zeros for byte alignment *app=0; app+=1; *app=0; app+=1; // 3D has three angles, 2D has one angle and thickness if(threeD) { // 3 material rotation angles in degrees *(double *)app=mpm[p]->GetRotationZInDegrees(); app+=sizeof(double); *(double *)app=mpm[p]->GetRotationYInDegrees(); app+=sizeof(double); *(double *)app=mpm[p]->GetRotationXInDegrees(); app+=sizeof(double); } else { // material rotation angle in degrees *(double *)app=mpm[p]->GetRotationZInDegrees(); app+=sizeof(double); // thickness (2D) in mm *(double *)app=mpm[p]->thickness(); app+=sizeof(double); } // (x,y,z) position (mm) *(double *)app=mpm[p]->pos.x; app+=sizeof(double); *(double *)app=mpm[p]->pos.y; app+=sizeof(double); if(threeD) { *(double *)app=mpm[p]->pos.z; app+=sizeof(double); } // original (x,y,z) position (mm) *(double *)app=mpm[p]->origpos.x; app+=sizeof(double); *(double *)app=mpm[p]->origpos.y; app+=sizeof(double); if(threeD) { *(double *)app=mpm[p]->origpos.z; app+=sizeof(double); } // velocity (mm/sec) if(mpmOrder[ARCH_Velocity]=='Y') { *(double *)app=mpm[p]->vel.x; app+=sizeof(double); *(double *)app=mpm[p]->vel.y; app+=sizeof(double); if(threeD) { *(double *)app=mpm[p]->vel.z; app+=sizeof(double); } } // stress - internally it is N/m^2 cm^3/g, output in N/m^2 or Pa // internal SI units are kPa/(kg/m^3) // For large deformation, need to convert Kirchoff Stress/rho0 to Cauchy stress int matid = mpm[p]->MatID(); rho0=theMaterials[matid]->rho; rho = rho0/theMaterials[matid]->GetCurrentRelativeVolume(mpm[p]); Tensor sp = mpm[p]->ReadStressTensor(); sxx=rho*sp.xx; syy=rho*sp.yy; sxy=rho*sp.xy; if(mpmOrder[ARCH_Stress]=='Y') { *(double *)app=sxx; app+=sizeof(double); *(double *)app=syy; app+=sizeof(double); *(double *)app=rho*sp.zz; app+=sizeof(double); *(double *)app=sxy; app+=sizeof(double); if(threeD) { *(double *)app=rho*sp.xz; app+=sizeof(double); *(double *)app=rho*sp.yz; app+=sizeof(double); } } // elastic strain (absolute) if(mpmOrder[ARCH_Strain]=='Y') { Tensor *ep=mpm[p]->GetStrainTensor(); *(double *)app=ep->xx; app+=sizeof(double); *(double *)app=ep->yy; app+=sizeof(double); *(double *)app=ep->zz; app+=sizeof(double); *(double *)app=ep->xy; app+=sizeof(double); if(threeD) { *(double *)app=ep->xz; app+=sizeof(double); *(double *)app=ep->yz; app+=sizeof(double); } } // plastic strain (absolute) if(mpmOrder[ARCH_PlasticStrain]=='Y') { Tensor *eplast=mpm[p]->GetPlasticStrainTensor(); *(double *)app=eplast->xx; app+=sizeof(double); *(double *)app=eplast->yy; app+=sizeof(double); *(double *)app=eplast->zz; app+=sizeof(double); *(double *)app=eplast->xy; app+=sizeof(double); if(threeD) { *(double *)app=eplast->xz; app+=sizeof(double); *(double *)app=eplast->yz; app+=sizeof(double); } } // external work (cumulative) in J if(mpmOrder[ARCH_WorkEnergy]=='Y') { *(double *)app=1.0e-6*mpm[p]->mp*mpm[p]->GetWorkEnergy(); app+=sizeof(double); } // temperature if(mpmOrder[ARCH_DeltaTemp]=='Y') { *(double *)app=mpm[p]->pTemperature; app+=sizeof(double); } // total plastic energy (Volume*energy) in J // energies in material point based on energy per unit mass // here need mass * U/(rho0 V0) if(mpmOrder[ARCH_PlasticEnergy]=='Y') { *(double *)app=1.0e-6*mpm[p]->mp*mpm[p]->GetPlastEnergy(); app+=sizeof(double); } // shear components (absolute) if(mpmOrder[ARCH_ShearComponents]=='Y') { *(double *)app=mpm[p]->GetDuDy(); app+=sizeof(double); *(double *)app=mpm[p]->GetDvDx(); app+=sizeof(double); } // total strain energy (Volume*energy) in J // energies in material point based on energy per unit mass // here need mass * U/(rho0 V0) // internal units are same as stress: N/m^2 cm^3/g = microJ/g = mJ/kg // note that rho*energy has units J/m^3 = N/m^2 (if rho in g/cm^3) if(mpmOrder[ARCH_StrainEnergy]=='Y') { *(double *)app=1.0e-6*mpm[p]->mp*mpm[p]->GetStrainEnergy(); app+=sizeof(double); } // material history data on particle (whatever units the material chooses) if(mpmOrder[ARCH_History]=='Y') { *(double *)app=theMaterials[mpm[p]->MatID()]->GetHistory(1,mpm[p]->GetHistoryPtr()); app+=sizeof(double); } else if(mpmOrder[ARCH_History]!='N') { if(mpmOrder[ARCH_History]&0x01) { *(double *)app=theMaterials[mpm[p]->MatID()]->GetHistory(1,mpm[p]->GetHistoryPtr()); app+=sizeof(double); } if(mpmOrder[ARCH_History]&0x02) { *(double *)app=theMaterials[mpm[p]->MatID()]->GetHistory(2,mpm[p]->GetHistoryPtr()); app+=sizeof(double); } if(mpmOrder[ARCH_History]&0x04) { *(double *)app=theMaterials[mpm[p]->MatID()]->GetHistory(3,mpm[p]->GetHistoryPtr()); app+=sizeof(double); } if(mpmOrder[ARCH_History]&0x08) { *(double *)app=theMaterials[mpm[p]->MatID()]->GetHistory(4,mpm[p]->GetHistoryPtr()); app+=sizeof(double); } } // concentration and gradients convert to wt fraction units using csat for this material if(mpmOrder[ARCH_Concentration]=='Y') { double csat=theMaterials[mpm[p]->MatID()]->concSaturation; *(double *)app=mpm[p]->pConcentration*csat; app+=sizeof(double); if(mpm[p]->pDiffusion!=NULL) { *(double *)app=mpm[p]->pDiffusion->Dc.x*csat; app+=sizeof(double); *(double *)app=mpm[p]->pDiffusion->Dc.y*csat; app+=sizeof(double); if(threeD) { *(double *)app=mpm[p]->pDiffusion->Dc.z*csat; app+=sizeof(double); } } else { *(double *)app=0.; app+=sizeof(double); *(double *)app=0.; app+=sizeof(double); if(threeD) { *(double *)app=0.; app+=sizeof(double); } } } // total heat energy (Volume*energy) in J // energies in material point based on energy per unit mass // here need mass * U/(rho0 V0) // internal units are same as stress: N/m^2 cm^3/g = microJ/g = mJ/kg // note that rho*energy has units J/m^3 = N/m^2 (if rho in g/cm^3) if(mpmOrder[ARCH_HeatEnergy]=='Y') { *(double *)app=1.0e-6*mpm[p]->mp*mpm[p]->GetHeatEnergy(); app+=sizeof(double); } // element crossings since last archive - now cumulative if(mpmOrder[ARCH_ElementCrossings]=='Y') { *(int *)app=mpm[p]->GetElementCrossings(); app+=sizeof(int); } // here=initial angle z (degrees) while angle(above)=here-0.5*180*wxy/PI (degrees) // Thus 0.5*180*wxy/PI = here-angle(above) or wxy = (PI/90)*(here-angle(above)) // here=initial angle y (degrees) while angle(above)=here+0.5*180*wrot.xz/PI_CONSTANT (degrees) // Thus 0.5*180*wxz/PI = -(here-angle(above)) or wxz = -(PI/90)*(here-angle(above)) // here=initial angle x (degrees) while angle(above)=here-0.5*180.*wrot.yz/PI_CONSTANT; (degrees) // Thus 0.5*180*wyz/PI = (here-angle(above)) or wyz = (PI/90)*(here-angle(above)) if(mpmOrder[ARCH_RotStrain]=='Y') { if(threeD) { *(double *)app=mpm[p]->GetAnglez0InDegrees(); app+=sizeof(double); *(double *)app=mpm[p]->GetAngley0InDegrees(); app+=sizeof(double); *(double *)app=mpm[p]->GetAnglex0InDegrees(); app+=sizeof(double); } else { *(double *)app=mpm[p]->GetAnglez0InDegrees(); app+=sizeof(double); } } // padding if(mpmRecSize<recSize) app+=recSize-mpmRecSize; // reversing bytes? if(fmobj->GetReverseBytes()) { app-=recSize; // defaults app+=Reverse(app,sizeof(int)); // element app+=Reverse(app,sizeof(double)); // mass app+=Reverse(app,sizeof(short))+2; // material ID app+=Reverse(app,sizeof(double)); // angle app+=Reverse(app,sizeof(double)); // thickness or dihedral app+=Reverse(app,sizeof(double)); // position app+=Reverse(app,sizeof(double)); if(threeD) app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); // orig position app+=Reverse(app,sizeof(double)); if(threeD) app+=Reverse(app,sizeof(double)); // velocity (mm/sec) if(mpmOrder[ARCH_Velocity]=='Y') { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); if(threeD) app+=Reverse(app,sizeof(double)); } // stress 2D (in N/m^2) if(mpmOrder[ARCH_Stress]=='Y') { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); if(threeD) { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); } } // strain 2D (absolute) if(mpmOrder[ARCH_Strain]=='Y') { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); if(threeD) { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); } } // plastic strain (absolute) if(mpmOrder[ARCH_PlasticStrain]=='Y') { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); if(threeD) { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); } } // work energy (cumulative) in J if(mpmOrder[ARCH_WorkEnergy]=='Y') app+=Reverse(app,sizeof(double)); // temperature if(mpmOrder[ARCH_DeltaTemp]=='Y') app+=Reverse(app,sizeof(double)); // total plastic energy if(mpmOrder[ARCH_PlasticEnergy]=='Y') app+=Reverse(app,sizeof(double)); // shear components if(mpmOrder[ARCH_ShearComponents]=='Y') { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); } // total strain energy if(mpmOrder[ARCH_StrainEnergy]=='Y') app+=Reverse(app,sizeof(double)); // material history data on particle if(mpmOrder[ARCH_History]=='Y') app+=Reverse(app,sizeof(double)); else if(mpmOrder[ARCH_History]!='N') { if(mpmOrder[ARCH_History]&0x01) app+=Reverse(app,sizeof(double)); if(mpmOrder[ARCH_History]&0x02) app+=Reverse(app,sizeof(double)); if(mpmOrder[ARCH_History]&0x04) app+=Reverse(app,sizeof(double)); if(mpmOrder[ARCH_History]&0x08) app+=Reverse(app,sizeof(double)); } // concentration and gradients if(mpmOrder[ARCH_Concentration]=='Y') { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); if(threeD) app+=Reverse(app,sizeof(double)); } // total strain energy if(mpmOrder[ARCH_HeatEnergy]=='Y') app+=Reverse(app,sizeof(double)); // element crossings if(mpmOrder[ARCH_ElementCrossings]=='Y') app+=Reverse(app,sizeof(int)); // rotational strain if(mpmOrder[ARCH_RotStrain]=='Y') { app+=Reverse(app,sizeof(double)); if(threeD) { app+=Reverse(app,sizeof(double)); app+=Reverse(app,sizeof(double)); } } // padding if(mpmRecSize<recSize) app+=recSize-mpmRecSize; } // write this particle (ofstream should buffer for us) try { afile.write(aptr,blen); if(afile.bad()) FileError("File error writing material point data",fname,"ArchiveData::ArchiveResults"); } catch(CommonException err) { // give up on hopefully temporary file problem cout << "# " << err.Message() << endl; cout << "# Will try to continue" << endl; afile.close(); free(aptr); return; } } // clear material point record buffer free(aptr); // add the cracks nextCrack=firstCrack; while(nextCrack!=NULL) { nextCrack->Archive(afile); nextCrack=(CrackHeader *)nextCrack->GetNextObject(); } // close the file try { afile.close(); if(afile.bad()) FileError("File error closing an archive file",fname,"ArchiveData::ArchiveResults"); } catch(CommonException err) { // give up on hopefully temporary file problem cout << "# " << err.Message() << endl; cout << "# Will try to continue" << endl; } }
// 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; }
// start results file before proceeding to analysis // throws CommonException() void CommonAnalysis::StartResultsOutput(void) { //-------------------------------------------------- // Analysis Title PrintAnalysisTitle(); cout << "Written by: Nairn Research Group, Oregon State University\n" << "Date: " << __DATE__ << "\n" << "Source: " << svninfo << "\n" << "Units: "; UnitsController::OutputUnits(); cout << "\n" #ifdef _OPENMP << "Processors: " << numProcs << "\n" #endif << endl; //-------------------------------------------------- // Description PrintSection("ANALYSIS DESCRIPTION"); cout << GetDescription() << endl; /* development flags * * Current Flags in Use in MPM Code * none * */ int i; bool hasFlags=FALSE; for(i=0; i<NUMBER_DEVELOPMENT_FLAGS; i++) { if(dflag[i]!=0) { cout << "Development Flag #" << i << " = " << dflag[i] << endl; cout << "... WARNING: unrecognized developer flag specified" << endl; hasFlags=TRUE; } } if(hasFlags) cout << endl; //-------------------------------------------------- // Analysis Type PrintAnalysisType(); // start section (Background Grid) #ifdef MPM_CODE PrintSection("NODES AND ELEMENTS (Background Grid)"); #else PrintSection("NODES AND ELEMENTS"); #endif //--------------------------------------------------- // Nodes char hline[200]; if(nnodes==0 || nelems<0) throw CommonException("No nodes or elements were defined in the input file.","CommonAnalysis::StartResultsOutput"); sprintf(hline,"Nodes: %d Elements: %d\n",nnodes,nelems); cout << hline; sprintf(hline,"DOF per node: %d ",nfree); cout << hline; // analysis type switch(np) { case PLANE_STRAIN: cout << "2D Plane Strain Analysis\n"; break; case PLANE_STRESS: cout << "2D Plane Stress Analysis\n"; break; case AXI_SYM: cout << "Axisymmetric Analysis\n"; break; case PLANE_STRAIN_MPM: cout << "2D Plane Strain MPM" << MPMAugmentation() << " Analysis\n"; break; case PLANE_STRESS_MPM: cout << "2D Plane Stress MPM" << MPMAugmentation() << " Analysis\n"; break; case THREED_MPM: cout << "3D MPM" << MPMAugmentation() << " Analysis\n"; break; case AXISYMMETRIC_MPM: cout << "Axisymmetric MPM" << MPMAugmentation() << " Analysis\n"; break; default: throw CommonException("No FEA or MPM analysis type was provided.","CommonAnalysis::StartResultsOutput"); } #ifdef MPM_CODE cout << "Incremental F Terms: " << MaterialBase::incrementalDefGradTerms << endl; #endif cout << endl; //--------------------------------------------------- // Nodes sprintf(hline,"NODAL POINT COORDINATES (in %s)",UnitsController::Label(CULENGTH_UNITS)); PrintSection(hline); archiver->ArchiveNodalPoints(np); //--------------------------------------------------- // Elements PrintSection("ELEMENT DEFINITIONS"); archiver->ArchiveElements(np); //--------------------------------------------------- // Defined Materials const char *err; #ifdef MPM_CODE sprintf(hline,"DEFINED MATERIALS\n (Note: moduli and stresses in %s, thermal exp. coeffs in ppm/K)\n ( density in %s)", UnitsController::Label(PRESSURE_UNITS),UnitsController::Label(DENSITY_UNITS)); #else sprintf(hline,"DEFINED MATERIALS\n (Note: moduli in %s, thermal exp. coeffs in ppm/K)", UnitsController::Label(PRESSURE_UNITS)); #endif PrintSection(hline); if(theMaterials==NULL) throw CommonException("No materials were defined.","CommonAnalysis::StartResultsOutput"); for(i=0; i<nmat; i++) { err=theMaterials[i]->VerifyAndLoadProperties(np); theMaterials[i]->PrintMaterial(i+1); cout << endl; if(err!=NULL) { cout << "Invalid material properties\n " << err << endl; throw CommonException("See material error above.","CommonAnalysis::StartResultsOutput"); } } // finish with analysis specific items MyStartResultsOutput(); //--------------------------------------------------- // initialize timers #ifdef _OPENMP startTime = omp_get_wtime(); #else time(&startTime); #endif startCPU=clock(); }
void NairnFEA::BuildStiffnessMatrix(void) { int i,j,iel,mi0,ni0,mi,ni,mj0,nj0,ind,mj,nj; int numnds,ii,jj; // allocate memory for stiffness matrix and zero it // st[] are pointers to rows of the stiffness matrix st = (double **)malloc(sizeof(double *)*(nsize+1)); if(st==NULL) throw CommonException("Memory error creating stiffness matrix pointers (st)", "NairnFEA::BuildStiffnessMatrix"); // allocate all in one contiguous block for better speed in algorithms stiffnessMemory = (double *)malloc(sizeof(double)*(nsize*nband)); if(stiffnessMemory==NULL) throw CommonException("Memory error creating stiffness matrix memory block", "NairnFEA::BuildStiffnessMatrix"); // allocate each row int baseAddr=0; for(i=1;i<=nsize;i++) { st[i]=&stiffnessMemory[baseAddr]; st[i]--; // to make it 1 based baseAddr+=nband; for(j=1;j<=nband;j++) st[i][j]=0.; } // Loop over all elements for(iel=0;iel<nelems;iel++) { // Get element stiffness matrix theElements[iel]->Stiffness(np); /*Transfer element stiffness matrix to global stiffness matrix m,n address in global and element matrices, respectively i,j refer to row and column 0 refers to base address */ numnds=theElements[iel]->NumberNodes(); for(i=1;i<=numnds;i++) { mi0=nfree*(theElements[iel]->NodeIndex(i)); ni0=nfree*(i-1); for(j=1;j<=numnds;j++) { if(theElements[iel]->NodeIndex(j)>=theElements[iel]->NodeIndex(i)) { mj0=nfree*(theElements[iel]->NodeIndex(j)); nj0=nfree*(j-1); for(ii=1;ii<=nfree;ii++) { mi=mi0+ii; ni=ni0+ii; for(jj=1;jj<=nfree;jj++) { mj=mj0+jj; ind=mj-mi+1; if(ind>0) { nj=nj0+jj; st[mi][ind]+=se[ni][nj]; } } } } } } /* Transfer element load vector into global load vector m,n are adresses in global and element vectors */ for(i=1;i<=numnds;i++) { mi0=nfree*(theElements[iel]->NodeIndex(i)); ni0=nfree*(i-1); for(ii=1;ii<=nfree;ii++) { mi=mi0+ii; ni=ni0+ii; rm[mi]+=re[ni]; } } } // terms for contraints with Lagrange multiplier DOFs if(firstConstraint!=NULL) { int nbase=nsize-numConstraints; Constraint *nextConstraint=firstConstraint; while(nextConstraint!=NULL) { numnds=nextConstraint->NumberNodes(); mj=nbase+nextConstraint->GetLambdaNum(); // mj > mi always for(i=1;i<=numnds;i++) { mi=nextConstraint->NodalDof(i,nfree); st[mi][mj-mi+1]+=nextConstraint->GetCoeff(i); } rm[mj]+=nextConstraint->GetQ(); nextConstraint=(Constraint *)nextConstraint->GetNextObject(); } } }
// finish start of FEA results file void NairnFEA::MyStartResultsOutput(void) { char hline[200]; int i; //--------------------------------------------------- // Temperature PrintSection("THERMAL LOAD"); if(temperatureExpr!=NULL) { if(!CreateFunction(temperatureExpr)) throw CommonException("The temperature expression is not a valid function","NairnFEA::MyStartResultsOutput"); for(i=1;i<=nnodes;i++) { nd[i]->gTemperature = FunctionValue(1,nd[i]->x,nd[i]->y,0.,0.,0.,0.)-stressFreeTemperature; } DeleteFunction(); } else { // unknown, but may have been set in explicit node commands temperatureExpr=new char[2]; strcpy(temperatureExpr,"?"); } sprintf(hline,"T0: %.2lf C\nT: %s C",stressFreeTemperature,temperatureExpr); cout << hline << endl; if(stressFreeTemperature!=0) { sprintf(hline,"T-T0: %s - %.2lf C",temperatureExpr,stressFreeTemperature); cout << hline << endl; } cout << endl; //--------------------------------------------------- // Fixed Displacements if(firstDispBC!=NULL) { PrintSection("NODAL POINTS WITH FIXED DISPLACEMENTS"); cout << " Node DOF Displacement (mm) Axis Angle\n" << "----------------------------------------------------\n"; NodalDispBC *nextBC=firstDispBC; while(nextBC!=NULL) nextBC=nextBC->PrintBC(cout); cout << endl; } //--------------------------------------------------- // Loaded Nodes if(firstLoadBC!=NULL) { PrintSection("NODAL POINTS WITH APPLIED LOADS"); cout << " Node DOF Load (N)\n" << "------------------------------\n"; NodalLoad *nextLoad=firstLoadBC; while(nextLoad!=NULL) nextLoad=nextLoad->PrintLoad(); cout << endl; } //--------------------------------------------------- // Stress element faces if(firstEdgeBC!=NULL) { PrintSection("FACES WITH APPLIED STRESS (MPa)"); cout << " Elem Fc Type Nodal Stress Nodal Stress Nodal Stress\n" << "----------------------------------------------------------------------\n"; EdgeBC *nextEdge=firstEdgeBC; while(nextEdge!=NULL) nextEdge=nextEdge->PrintEdgeLoad(); cout << endl; } //--------------------------------------------------- // Periodic directions if(periodic.dof) { PrintSection("PERIODIC DIRECTIONS"); // x periodic, but y not (not allowed for axisymmetric) if(fmobj->periodic.dof==1) { sprintf(hline,"x direction from %g to %g mm",periodic.xmin,periodic.xmax); cout << hline << endl; if(periodic.fixDu) { sprintf(hline," Displacement jump fixed at %g mm for exx = %g",periodic.du,periodic.du/(periodic.xmax-periodic.xmin)); cout << hline << endl; } if(periodic.fixDudy) { sprintf(hline," Displacement jump slope fixed at %g",periodic.dudy); cout << hline << endl; } } // y periodic, but x not (only option for axisymmetrix - z periodic, but r not) else if(fmobj->periodic.dof==2) { char pax = fmobj->IsAxisymmetric() ? 'z' : 'y' ; sprintf(hline,"%c direction from %g to %g mm",pax,periodic.ymin,periodic.ymax); cout << hline << endl; if(periodic.fixDv) { sprintf(hline," Displacement jump fixed at %g mm for e%c%c = %g",periodic.dv,pax,pax,periodic.dv/(periodic.ymax-periodic.ymin)); cout << hline << endl; } if(periodic.fixDvdx) { sprintf(hline," Displacement jump slope fixed at %g",periodic.dvdx); cout << hline << endl; } } // x and y both periodic (not allowed for axisymmetric) else { sprintf(hline,"x direction from %g to %g mm",periodic.xmin,periodic.xmax); cout << hline << endl; if(periodic.fixDu) { sprintf(hline," Displacement jump fixed at %g mm for exx = %g",periodic.du,periodic.du/(periodic.xmax-periodic.xmin)); cout << hline << endl; } if(periodic.fixDvdx) { sprintf(hline," Displacement jump dv fixed at %g",periodic.dvdx); cout << hline << endl; } sprintf(hline,"y direction from %g to %g mm",periodic.ymin,periodic.ymax); cout << hline << endl; if(periodic.fixDv) { sprintf(hline," Displacement jump fixed at %g mm for eyy = %g",periodic.dv,periodic.dv/(periodic.ymax-periodic.ymin)); cout << hline << endl; } if(periodic.fixDudy) { sprintf(hline," Displacement jump du fixed at %g",periodic.dudy); cout << hline << endl; // if both Dudy and Dvdx then global shear is specified if(periodic.fixDvdx) { double b=periodic.dvdx/(periodic.xmax-periodic.xmin); double d=periodic.dudy/(periodic.ymax-periodic.ymin); sprintf(hline," for gxy = %g and rotation = %g",b+d,(d-b)/2); cout << hline << endl; } } } cout << endl; } }
// To support CPDI find nodes in the particle domain, find their elements, // their natural coordinates, and weighting values for gradient calculations // Should be done only once per time step and here only for axisynmmetric // and linear CPDI // throws CommonException() if particle corner has left the grid void MatPointAS::GetCPDINodesAndWeights(int cpdiType) { // get polygon vectors - these are from particle to edge // and generalize semi width lp in 1D GIMP Vector r1,r2,c; GetSemiSideVectors(&r1,&r2,NULL); // truncate domain by shrinking if any have x < 0, but keep particle in the middle if(pos.x-fabs(r1.x+r2.x)<0.) { // make pos.x-shrink*fabs(r1.x+r2.x) very small and positive double shrink = (pos.x-mpmgrid.GetCellXSize()*1.e-10)/fabs(r1.x+r2.x); r1.x *= shrink; r1.y *= shrink; r2.x *= shrink; r2.y *= shrink; } // Particle domain area is area of the full parallelogram // Assume positive due to orientation of initial vectors, and sign probably does not matter double Ap = 4.*(r1.x*r2.y - r1.y*r2.x); try { // nodes at four courves in ccw direction c.x = pos.x-r1.x-r2.x; c.y = pos.y-r1.y-r2.y; cpdi[0]->inElem = mpmgrid.FindElementFromPoint(&c)-1; theElements[cpdi[0]->inElem]->GetXiPos(&c,&cpdi[0]->ncpos); c.x = pos.x+r1.x-r2.x; c.y = pos.y+r1.y-r2.y; cpdi[1]->inElem = mpmgrid.FindElementFromPoint(&c)-1; theElements[cpdi[1]->inElem]->GetXiPos(&c,&cpdi[1]->ncpos); c.x = pos.x+r1.x+r2.x; c.y = pos.y+r1.y+r2.y; cpdi[2]->inElem = mpmgrid.FindElementFromPoint(&c)-1; theElements[cpdi[2]->inElem]->GetXiPos(&c,&cpdi[2]->ncpos); c.x = pos.x-r1.x+r2.x; c.y = pos.y-r1.y+r2.y; cpdi[3]->inElem = mpmgrid.FindElementFromPoint(&c)-1; theElements[cpdi[3]->inElem]->GetXiPos(&c,&cpdi[3]->ncpos); // shape weights double rxsum = r1.x+r2.x; double rxdiff = r1.x-r2.x; cpdi[0]->ws = 0.25 - rxsum/(12.*pos.x); cpdi[1]->ws = 0.25 + rxdiff/(12.*pos.x); cpdi[2]->ws = 0.25 + rxsum/(12.*pos.x); cpdi[3]->ws = 0.25 - rxdiff/(12.*pos.x); // gradient weighting values Ap = 1./Ap; double beta = r1.x*r1.y - r2.x*r2.y; double rysum = r1.y+r2.y; double rydiff = r1.y-r2.y; cpdi[0]->wg.x = (rydiff - beta/(3.*pos.x))*Ap; cpdi[0]->wg.y = -rxdiff*Ap*4.*cpdi[0]->ws; double tipwt = 0.25/pos.x; cpdi[0]->wg.z = tipwt; cpdi[1]->wg.x = (rysum + beta/(3.*pos.x))*Ap; cpdi[1]->wg.y = -rxsum*Ap*4.*cpdi[1]->ws; cpdi[1]->wg.z = tipwt; cpdi[2]->wg.x = -(rydiff + beta/(3.*pos.x))*Ap; cpdi[2]->wg.y = rxdiff*Ap*4.*cpdi[2]->ws; cpdi[2]->wg.z = tipwt; cpdi[3]->wg.x = -(rysum - beta/(3.*pos.x))*Ap; cpdi[3]->wg.y = rxsum*Ap*4.*cpdi[3]->ws;; cpdi[3]->wg.z = tipwt; } catch(CommonException err) { char msg[200]; sprintf(msg,"A CPDI particle domain node has left the grid: %s",err.Message()); throw CommonException(msg,"MatPointAS::GetCPDINodesAndWeights"); } // traction BC area saves 1/2 surface area of particle domain on the various edges if(faceArea!=NULL) { faceArea->x = sqrt(r1.x*r1.x+r1.y*r1.y)*mpmgrid.GetThickness(); // edges 1 and 3 faceArea->y = sqrt(r2.x*r2.x+r2.y*r2.y)*mpmgrid.GetThickness(); // edges 2 and 4 } }
// To support traction boundary conditions, find the deformed edge, natural coordinates of // the corners along the edge, elements for those edges, and a normal vector in direction // of the traction // return ratio of second nodal weight to first one // throws CommonException() if traction edge has left the grid double MatPointAS::GetTractionInfo(int face,int dof,int *cElem,Vector *corners,Vector *tscaled,int *numDnds) { *numDnds = 2; double faceWt,ratio=1.,r1mag,r2mag; double rp=pos.x; Vector r1,r2; if(ElementBase::useGimp==UNIFORM_GIMP_AS) { double r1x,r2y; GetUndeformedSemiSides(&r1x,&r2y,NULL); r1.x = r1x; r1.y = 0.; r2.x = 0.; r2.y = r2y; // truncate if extends into r<0 if(pos.x-r1.x<0.) { r1.x = 0.5*(pos.x+r1.x); rp = r1.x; } // get magnitudes r1mag = r1.x; r2mag = r2.y; } else { // always LINEAR_CPDI_AS // get polygon vectors - these are from particle to edge // and generalize semi width lp in 1D GIMP GetSemiSideVectors(&r1,&r2,NULL); // truncate by shrinking domain if any have x < 0, but keep particle in the middle if(pos.x-fabs(r1.x+r2.x)<0.) { // make pos.x-shrink*fabs(r1.x+r2.x) very small and positive double shrink = (pos.x-mpmgrid.GetCellXSize()*1.e-10)/fabs(r1.x+r2.x); r1.x *= shrink; r1.y *= shrink; r2.x *= shrink; r2.y *= shrink; } // get magnitudes r1mag = sqrt(r1.x*r1.x + r1.y*r1.y); r2mag = sqrt(r2.x*r2.x + r2.y*r2.y); } // Find corners Vector c1,c2; switch(face) { case 1: // lower edge c1.x = rp-r1.x-r2.x; // node 1 c1.y = pos.y-r1.y-r2.y; c2.x = rp+r1.x-r2.x; // node 2 c2.y = pos.y+r1.y-r2.y; faceWt = r1mag*(rp - r2.x - r1.x/3.); // node 1, node 2 should be plus ratio = r1mag*(rp - r2.x + r1.x/3.)/faceWt; // find the ratio break; case 2: // right edge c1.x = rp+r1.x-r2.x; // node 2 c1.y = pos.y+r1.y-r2.y; c2.x = rp+r1.x+r2.x; // node 3 c2.y = pos.y+r1.y+r2.y; faceWt = r2mag*(rp + r1.x - r2.x/3.); // node 2, node 3 should be plus ratio = r2mag*(rp + r1.x + r2.x/3.)/faceWt; // find the ratio break; case 3: // top edge c1.x = rp+r1.x+r2.x; // node 3 c1.y = pos.y+r1.y+r2.y; c2.x = rp-r1.x+r2.x; // node 4 c2.y = pos.y-r1.y+r2.y; faceWt = r1mag*(rp + r2.x + r1.x/3.); // node 3, node 4 should be minus ratio = r1mag*(rp + r2.x - r1.x/3.)/faceWt; // find the ratio break; default: // left edge c1.x = rp-r1.x+r2.x; // node 4 c1.y = pos.y-r1.y+r2.y; c2.x = rp-r1.x-r2.x; // node 1 c2.y = pos.y-r1.y-r2.y; faceWt = r2mag*(rp - r1.x + r2.x/3.); // node 4, node 1 should be minus ratio = r2mag*(rp - r1.x - r2.x/3.)/faceWt; // find the ratio break; } // get elements try { cElem[0] = mpmgrid.FindElementFromPoint(&c1)-1; theElements[cElem[0]]->GetXiPos(&c1,&corners[0]); cElem[1] = mpmgrid.FindElementFromPoint(&c2)-1; theElements[cElem[1]]->GetXiPos(&c2,&corners[1]); } catch(CommonException err) { char msg[200]; sprintf(msg,"A Traction edge node has left the grid: %s",err.Message()); throw CommonException(msg,"MatPointAS::GetTractionInfo"); } // get traction normal vector by radial integral for first node no the edge ZeroVector(tscaled); double ex,ey,enorm; switch(dof) { case 1: // normal is x direction tscaled->x = faceWt; break; case 2: // normal is y direction tscaled->y = faceWt; break; case N_DIRECTION: // cross product of edge vector with (0,0,1) = (ey, -ex) ex = c2.y-c1.y; ey = c1.x-c2.x; case T1_DIRECTION: if(dof==T1_DIRECTION) { ex = c2.x-c1.x; ey = c2.y-c1.y; } // load in direction specified by normalized (ex,ey) enorm = sqrt(ex*ex+ey*ey); tscaled->x = ex*faceWt/enorm; tscaled->y = ey*faceWt/enorm; break; default: // normal is z direction (not used here) tscaled->z = faceWt; break; } // return ratio of second nodal weight to first one return ratio; }
// Adjust time step now CustomTask *CarnotCycle::StepCalculation(void) { int p; MaterialBase *matID; double Vrel = 0.; int numgas = 0; double Tgas = 0.; // loop over nonrigid material points for(p=0;p<nmpmsNR;p++) { numgas++; Vrel += mpm[p]->GetRelativeVolume(); Tgas += mpm[p]->pPreviousTemperature; } // get averages Vrel /= (double)numgas; Tgas /= (double)numgas; switch(carnotStep) { case 0: carnotStep = 1; cout << "# Step 1: isothermal expansion" << endl; ConductionTask::adiabatic = FALSE; break; case 1: // stop when Vrel reaches V1rel if(Vrel >= V1rel) { // switch to adibatic expanion carnotStep = 2; ConductionTask::adiabatic = TRUE; cout << "# Step 2: adibatic expansion" << endl; } break; case 2: // stop when T cools to T2 if(Tgas<=T2) { // switch to pause carnotStep = 3; ConductionTask::adiabatic = FALSE; V2rel = Vrel; // find velocity for(p=0;p<nmpms;p++) { // verify material is defined and sets if field number (in in multimaterial mode) matID=theMaterials[mpm[p]->MatID()]; // material object for this particle if(matID->Rigid()) mpm[p]->ReverseParticle(false,false); else mpm[p]->StopParticle(); } // estimate next volume if(V3rel<1.) V3rel = V2rel/V1rel; cout << "# Step 2: isothermal compression to " << V3rel << "*V0" << endl; } break; case 3: // stop when Vrel reaches V3rel if(Vrel <= V3rel) { // switch to adibatic compression carnotStep = 4; ConductionTask::adiabatic = TRUE; cout << "# Step 4: adibatic compression" << endl; } break; case 4: // done when T increases to thermal.reference, or when volume returns to 1 if(Tgas>=thermal.reference) throw CommonException("Carnot cycle is complete","CarnotCycle::StepCalculation()"); //if(Vrel<=1.) // throw CommonException("Carnot cycle is complete","CarnotCycle::StepCalculation()"); break; default: break; } return nextTask; }
// 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 }