// Calculate J and K at crack tips
CustomTask *PropagateTask::StepCalculation(void)
{
    // if not needed, just exit
    if(!doPropCalcs) return nextTask;
	
	// particle extrapolation if needed
    if(doEnergyBalanceCalcs)
    {   totalPlastic = 0.;
        totalPotential = 0.;
        for(int p=0;p<nmpmsNR;p++)
        {   MPMBase *mpnt = mpm[p];
            
            // track total energies in J = N-m
            //	mp is g, stored energy is N/m^2 cm^3/g, vel is mm/sec
            // workEnergy in J =  1.0e-6*mp*mpm[p]->GetWorkEnergy()
            // plastic 1.0e-6*mp*mpm[p]->GetPlastEnergy()
            // external work 1.e-9*mpm[p]->GetExtWork()
            // kinetic energy 0.5e-9*mp*(vel.x*vel.x+vel.y*vel.y)
            
            // plastic energy per unit thickness (units of N) (only needed energy balance crack growth)
            double mp = mpnt->mp;
            totalPlastic += 1.0e-3*mp*mpnt->GetPlastEnergy()/mpnt->thickness();
            //totalPotential += 1.0e-3*(mp*mpnt->GetStrainEnergy()
            //                        + 0.5e-3*mp*(mpnt->vel.x*mpnt->vel.x+mpnt->vel.y*mpnt->vel.y)
            //                        - 1.e-3*mpnt->GetExtWork())/mpnt->thickness();
			throw "external work is no longer available";
        }
    }
    
    CrackHeader *nextCrack;
    CrackSegment *crkTip;
    double cSize;
    int i,inMat;
    Vector tipDir,growTo,grow;
    int tipElem;
	char isAlt[10];
    
    // loop over cracks
    nextCrack=firstCrack;
    while(nextCrack!=NULL)
    {	// each crack tip
        for(i=START_OF_CRACK;i<=END_OF_CRACK;i++)
        {   // find crack tip and direction
			nextCrack->CrackTipAndDirection(i,&crkTip,tipDir);
            
            // crack propagation
            inMat=crkTip->tipMatnum;
            if(inMat>0)
            {	// crack tip terms last two times propagated
                crkTip->potential[2]=crkTip->potential[1];
                crkTip->potential[1]=crkTip->potential[0];
                crkTip->potential[0]=totalPotential;
                crkTip->plastic[2]=crkTip->plastic[1];
                crkTip->plastic[1]=crkTip->plastic[0];
                crkTip->plastic[0]=totalPlastic;
                crkTip->clength[2]=crkTip->clength[1];
                crkTip->clength[1]=crkTip->clength[0];
                crkTip->clength[0]=nextCrack->Length();
            
                // see if it grows
				int shouldGo=theMaterials[inMat-1]->ShouldPropagate(crkTip,tipDir,nextCrack,fmobj->np,0);
				isAlt[0] = 0;
				if(shouldGo==GROWNOW)
				{	nextCrack->SetAllowAlternate(i,FALSE);
				}
				else if(nextCrack->GetAllowAlternate(i))
				{	shouldGo=theMaterials[inMat-1]->ShouldPropagate(crkTip,tipDir,nextCrack,fmobj->np,1);
					if(shouldGo==GROWNOW) strcpy(isAlt," (alt)");
				}
				if(shouldGo==GROWNOW)
                {   theResult=GROWNOW;
                    tipElem=crkTip->planeInElem-1;
                    if(fabs(tipDir.x)>fabs(tipDir.y))
                        cSize=theElements[tipElem]->xmax-theElements[tipElem]->xmin;
                    else
                        cSize=theElements[tipElem]->ymax-theElements[tipElem]->ymin;
                    grow.x=cellsPerPropagationStep*cSize*tipDir.x;
                    grow.y=cellsPerPropagationStep*cSize*tipDir.y;
					
					// adjust if crossing another crack
					double p = nextCrack->AdjustGrowForCrossing(&grow,crkTip);
					
					// crack number and tip
					archiver->IncrementPropagationCounter();
                    cout << "# propagation" << isAlt << " crack-tip " << nextCrack->GetNumber() << "-" << i;
					
					// summarize
					cout << " at t=" << 1000*mtime << " with J=Jtip+Jzone : " << 1000.*crkTip->Jint.z <<
							" = " << 1000.*crkTip->Jint.x << " + " << 1000.*(crkTip->Jint.z-crkTip->Jint.x) << endl;
                    
                    // if jump is .7 or more cells, make more than 1 segment
                    int iseg,numSegs = 1;
                    if(p*cellsPerPropagationStep>.7) numSegs= 2*(p*cellsPerPropagationStep+.25);
                    CrackSegment *newCrkTip;
                    for(iseg=1;iseg<=numSegs;iseg++)
                    {   growTo.x=crkTip->x+(double)iseg*grow.x/(double)numSegs;
                        growTo.y=crkTip->y+(double)iseg*grow.y/(double)numSegs;
                        if(fmobj->dflag[0]==4) growTo.y=0.;			// force cutting simulation to stay in cut plane at 0
                        newCrkTip=nextCrack->Propagate(growTo,(int)i,theMaterials[inMat-1]->tractionMat[0]);
                    }
                    crkTip = newCrkTip;
                    
					if(crkTip!=NULL)
					{	// check if crack speed is being controlled
						if(theMaterials[inMat-1]->ControlCrackSpeed(crkTip,propTime))
							nextPropTime=mtime+propTime;
						
						// crack tip heating (if activated)
						if(ConductionTask::active)
							conduction->StartCrackTipHeating(crkTip,grow,nextCrack->GetThickness());
					}
                }
                else
                {   // when no growth, restore previous growth results
                    crkTip->potential[0]=crkTip->potential[1];
                    crkTip->potential[1]=crkTip->potential[2];
                    crkTip->plastic[0]=crkTip->plastic[1];
                    crkTip->plastic[1]=crkTip->plastic[2];
                    crkTip->clength[0]=crkTip->clength[1];
                    crkTip->clength[1]=crkTip->clength[2];
                }
            }
        }

        // next crack
        nextCrack=(CrackHeader *)nextCrack->GetNextObject();
    }
    
    return nextTask;
}
// Calculate J and K at crack tips
CustomTask *PropagateTask::StepCalculation(void)
{
    // if not needed, just exit
    if(!doPropCalcs) return nextTask;
	
    CrackHeader *nextCrack;
    CrackSegment *crkTip;
    double cSize;
    int i,inMat;
    Vector tipDir,growTo,grow;
    int tipElem;
	char isAlt[10];
    
    // loop over cracks
    nextCrack=firstCrack;
    while(nextCrack!=NULL)
    {	// each crack tip
        for(i=START_OF_CRACK;i<=END_OF_CRACK;i++)
        {   // find crack tip and direction
			nextCrack->CrackTipAndDirection(i,&crkTip,tipDir);
            
            // crack propagation
            inMat=crkTip->tipMatnum;
            if(inMat>0)
            {	// see if it grows
				int shouldGo=theMaterials[inMat-1]->ShouldPropagate(crkTip,tipDir,nextCrack,fmobj->np,0);
				isAlt[0] = 0;
				if(shouldGo==GROWNOW)
				{	nextCrack->SetAllowAlternate(i,false);
				}
				else if(nextCrack->GetAllowAlternate(i))
				{	shouldGo=theMaterials[inMat-1]->ShouldPropagate(crkTip,tipDir,nextCrack,fmobj->np,1);
					if(shouldGo==GROWNOW) strcpy(isAlt," (alt)");
				}
				if(shouldGo==GROWNOW)
                {   theResult=GROWNOW;
                    tipElem=crkTip->planeElemID();
                    if(fabs(tipDir.x)>fabs(tipDir.y))
                        cSize=theElements[tipElem]->xmax-theElements[tipElem]->xmin;
                    else
                        cSize=theElements[tipElem]->ymax-theElements[tipElem]->ymin;
                    grow.x=cellsPerPropagationStep*cSize*tipDir.x;
                    grow.y=cellsPerPropagationStep*cSize*tipDir.y;
					
					// adjust if crossing another crack - adjusts grow is need and returns resulting relative change (in p)
					double p = nextCrack->AdjustGrowForCrossing(&grow,crkTip,cSize,&tipDir);
					
					// crack number and tip
					archiver->IncrementPropagationCounter();
                    cout << "# propagation" << isAlt << " crack-tip " << nextCrack->GetNumber() << "-" << i;
					
					// summarize
					cout << " at t=" << mtime*UnitsController::Scaling(1.e3)
						<< " with J=Jtip+Jzone : " << crkTip->Jint.z*UnitsController::Scaling(1.e-3)
						<< " = " << crkTip->Jint.x*UnitsController::Scaling(1.e-3)
						<< " + " << (crkTip->Jint.z-crkTip->Jint.x)*UnitsController::Scaling(1.e-3) << endl;
                    
                    // if jump is .7 or more cells, divide propagation into multiple segments
                    int iseg,numSegs = 1;
                    if(p*cellsPerPropagationStep>.7) numSegs= (int)(2*(p*cellsPerPropagationStep+.25));
                    CrackSegment *newCrkTip = NULL;
                    for(iseg=1;iseg<=numSegs;iseg++)
                    {   growTo.x=crkTip->x+(double)iseg*grow.x/(double)numSegs;
                        growTo.y=crkTip->y+(double)iseg*grow.y/(double)numSegs;
                        newCrkTip=nextCrack->Propagate(growTo,(int)i,theMaterials[inMat-1]->tractionMat[0]);
                    }
                    crkTip = newCrkTip;
                    
					if(crkTip!=NULL)
					{	// check if crack speed is being controlled
						if(theMaterials[inMat-1]->ControlCrackSpeed(crkTip,propTime))
							nextPropTime=mtime+propTime;
						
						// crack tip heating (if activated)
						if(ConductionTask::active)
							conduction->StartCrackTipHeating(crkTip,grow,nextCrack->GetThickness());
					}
                }
            }
        }

        // next crack
        nextCrack=(CrackHeader *)nextCrack->GetNextObject();
    }
    
    return nextTask;
}