Esempio n. 1
0
// calculate tractions on one side of crack for this segment
// add forces to material velocity fields on one side of the crack
double CrackSegment::AddTractionForceSegSide(CrackHeader *theCrack,int side,double sign)
{
	int numnds,nds[maxShapeNodes];
    double fn[maxShapeNodes];
    short vfld;
	double fnorm = 0.;
	NodalPoint *ndi;
	int cnum=theCrack->GetNumber();

	Vector cspos = MakeVector(surfx[side-1], surfy[side-1], 0.);
	const ElementBase *elref = theElements[surfInElem[side-1]];
	elref->GetShapeFunctionsForCracks(&numnds,fn,nds,&cspos);
	
	// loop over all nodes seen by this crack surface particle
	for(int i=1;i<=numnds;i++)
	{	// Get velocity field to use
		ndi = nd[nds[i]];
		vfld = ndi->GetFieldForSurfaceParticle(side,cnum,this);
		
		// if has particles (and they see cracks), add force and track normalization
		if(vfld>=0)
		{	ndi->AddFtotSpreadTask3(vfld,FTract(sign*fn[i]));
			fnorm += fn[i];
		}
	}
	
	// return amount used
	return fnorm;
}
// Get mass matrix, find dimensionless particle locations,
//	and find grid momenta
void PostForcesTask::Execute(void)
{
	// Add traction BCs on particles
	MatPtTractionBC::SetParticleSurfaceTractions(mtime);
	
	// Add traction law forces to velocity fields
	if(fmobj->hasTractionCracks)
	{	CrackHeader *nextCrack=firstCrack;
		while(nextCrack!=NULL)
		{	nextCrack->AddTractionForce();
			nextCrack=(CrackHeader *)nextCrack->GetNextObject();
		}
	}
	
	// Add crack tip heating adds to conduction force
	if(conduction) conduction->AddCrackTipHeating();
	
	// Add interface forces to velocity fields and track total interface energy
    NodalPoint::interfaceEnergy=0.;
    CrackNode::CrackInterfaceOnKnownNodes();
    MaterialInterfaceNode::MaterialInterfaceOnKnownNodes();
    
	// Add gravity and body forces (if any are present)
	// Note: If ever need to implement body force that depend on particle state (stress, strain, etc.)
	//			then move the body force addition into GridForcesTask loop where gravity is commented out
    // When used to keep Fext, this section would all add fint and fext to get ftot (and it was always needed)
	Vector gridBodyForce;
	if(bodyFrc.gravity || bodyFrc.hasGridBodyForce)
	{   for(int i=1;i<=nnodes;i++)
		{	NodalPoint *ndptr = nd[i];
			bodyFrc.GetGridBodyForce(&gridBodyForce,ndptr,mtime);
			ndptr->AddGravityAndBodyForceTask3(&gridBodyForce);
		}
	}
	
    // Impose BCs on ftot to get correct grid BCs for velocity
    NodalVelBC::ConsistentGridForces();
	
	// Do similar to transport property BCs (not parallel because small and possible use of function/global variables)
	TransportTask *nextTransport=transportTasks;
	while(nextTransport!=NULL)
		nextTransport=nextTransport->SetTransportForceBCs(timestep);
}
// Update grid momenta and transport rates
void UpdateMomentaTask::Execute(void)
{
	CommonException *umErr = NULL;
	
#pragma omp parallel for
	for(int i=1;i<=nnodes;i++)
	{	NodalPoint *ndptr = nd[i];
		ndptr->UpdateMomentaOnNode(timestep);
		
		// get grid transport rates (update transport properties when particle state updated)
		// do first so both material and crack contact will have actual rates
		TransportTask *nextTransport=transportTasks;
		while(nextTransport!=NULL)
			nextTransport=nextTransport->TransportRates(ndptr,timestep);

		// material contact
		if(fmobj->multiMaterialMode)
		{	try
			{	ndptr->MaterialContactOnNode(timestep,UPDATE_MOMENTUM_CALL,NULL,NULL);
			}
			catch(CommonException err)
			{	if(umErr==NULL)
				{
#pragma omp critical
					umErr = new CommonException(err);
				}
			}
		}
		
	}
	
	// throw error now
	if(umErr!=NULL) throw *umErr;
		
	// adjust momenta and forces for crack contact on known nodes
	CrackNode::CrackContactTask4(timestep);
	
}
// 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();
	}
}
Esempio n. 5
0
// 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);
	
}	
// 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);
}
Esempio n. 7
0
// 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;
}
Esempio n. 8
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 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);

}
Esempio n. 9
0
// 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);
}
// 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();
	}
    
}
// Get mass matrix, find dimensionless particle locations,
//	and find grid momenta
// throws CommonException()
void PostExtrapolationTask::Execute(void)
{
	CommonException *massErr = NULL;
	
	bool combineRigid = firstCrack!=NULL && fmobj->multiMaterialMode && fmobj->hasRigidContactParticles;
	
	// 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
            {	// combine rigid fields if necessary
                if(combineRigid)
                    ndptr->CopyRigidParticleField();
				// 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(std::bad_alloc&)
			{	if(massErr==NULL)
				{
#pragma omp critical (error)
					massErr = new CommonException("Memory error","PostExtrapolationTask::Execute");
				}
			}
			catch(...)
			{	if(massErr==NULL)
				{
#pragma omp critical (error)
					massErr = new CommonException("Unexpected error","PostExtrapolationTask::Execute");
				}
			}
		}
		
#pragma omp critical (linknodes)
		{
			// 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;
        //cout << "# Find Reflected Nodes" << endl;
        while(nextBC!=NULL)
            nextBC = nextBC->SetMirroredVelBC(mtime);
    }
	
	// used to call class methods for material contact and crack contact here
	// Impose velocity BCs
	NodalVelBC::GridMomentumConditions(TRUE);
}