/*
  Assuming this cond func is called in the Element IC 
  to assign spatially varying amaterial_I. 

  Make this a template for other situations.
*/
void _SnacCondFunc_AssignPhaseID( Index element_lI, Variable_Index var_I, void* _context, void* result ){
	Snac_Context*		context = (Snac_Context*)_context;
	Snac_Element*		element = Snac_Element_At( context, element_lI );
	Material_Index*		material_I = (Material_Index*)result;
	
	/* Stuff for convenience */
	Mesh*				mesh = context->mesh;
	MeshLayout*			layout = (MeshLayout*)mesh->layout;
	HexaMD*				decomp = (HexaMD*)layout->decomp;
	IJK					ijk;
	Element_GlobalIndex	element_gI = _MeshDecomp_Element_LocalToGlobal1D( decomp, element_lI );
	Index				oneThird = (unsigned int)((11.0/30.0)*decomp->elementGlobal3DCounts[0]);
	Index				twoThirds = (unsigned int)((19.0/30.0)*decomp->elementGlobal3DCounts[0]);

	RegularMeshUtils_Element_1DTo3D( decomp, element_gI, &ijk[0], &ijk[1], &ijk[2] );

	fprintf(stderr,"Running %s\n",__func__);
	
	if( ijk[0] < oneThird )
		(*material_I) = 0;
	else if( ijk[0] >= oneThird && ijk[0] <= twoThirds )
		(*material_I) = 1;
	else
		(*material_I) = 0;
}
PetscErrorCode KSPBuildPressure_CB_Nullspace_BSSCR(KSP ksp)
{
    KSP_BSSCR        *bsscr = (KSP_BSSCR *)ksp->data;
    FeEquationNumber *eq_num = bsscr->solver->st_sle->pSolnVec->feVariable->eqNum;
    FeMesh           *feMesh = bsscr->solver->st_sle->pSolnVec->feVariable->feMesh; /* is the pressure mesh */
    unsigned          ijk[3];
    Vec               t, v;
    int numLocalNodes, globalNodeNumber, i, j, eq;
    MatStokesBlockScaling BA = bsscr->BA;
    PetscErrorCode ierr;
    Mat            Amat,Pmat, G;
    MatStructure   pflag;

    PetscFunctionBegin;
    /* get G matrix from Amat matrix operator on ksp */
    ierr=Stg_PCGetOperators(ksp->pc,&Amat,&Pmat,&pflag);CHKERRQ(ierr);
    MatNestGetSubMat( Amat, 0,1, &G );/* G should always exist */
    /* now create Vecs t and v to match size of G: i.e. pressure */ /* NOTE: not using "h" vector from ksp->vec_rhs because this part of the block vector doesn't always exist */
    MatGetVecs( G, &t, PETSC_NULL );/* t and v are destroyed in KSPDestroy_BSSCR */
    MatGetVecs( G, &v, PETSC_NULL );/* t and v such that can do G*t */

    numLocalNodes = Mesh_GetLocalSize( feMesh,  MT_VERTEX); /* number of nodes on current proc not counting any shadow nodes */
    for(j=0;j<numLocalNodes;j++){
	i = globalNodeNumber = Mesh_DomainToGlobal( feMesh, MT_VERTEX, j);
	RegularMeshUtils_Element_1DTo3D(feMesh, i, ijk);
	eq = eq_num->destinationArray[j][0];/* get global equation number -- 2nd arg is always 0 because pressure has only one dof */
	if(eq != -1){
	    if( (ijk[0]+ijk[1]+ijk[2])%2 ==0 ){
		VecSetValue(t,eq,1.0,INSERT_VALUES);
	    }
	    else{
		VecSetValue(v,eq,1.0,INSERT_VALUES);	    }}}

    VecAssemblyBegin( t );
    VecAssemblyEnd( t );
    VecAssemblyBegin( v );
    VecAssemblyEnd( v );

    /* Scaling the null vectors here because it easier at the moment *//* maybe should do this in the original scaling function */
    if( BA->scaling_exists == PETSC_TRUE ){
	Vec R2;
	/* Get the scalings out the block mat data */
	VecNestGetSubVec( BA->Rz, 1, &R2 );
	VecPointwiseDivide( t, t, R2); /* x <- x * 1/R2 */
	VecPointwiseDivide( v, v, R2);
    }

    bsscr_writeVec( t, "t", "Writing t vector");
    bsscr_writeVec( v, "v", "Writing v vector");

    bsscr->t=t;
    bsscr->v=v;

    PetscFunctionReturn(0);
}
void _SnacCondFunc_DeadSea( Index element_lI, Variable_Index var_I, void* _context, void* result ){
	Snac_Context*		context = (Snac_Context*)_context;
	Snac_Element*		element = Snac_Element_At( context, element_lI );
	Material_Index*		material_I = (Material_Index*)result;
	
	/* Stuff for convenience */
	Mesh*				mesh = context->mesh;
	MeshLayout*			layout = (MeshLayout*)mesh->layout;
	HexaMD*				decomp = (HexaMD*)layout->decomp;
	IJK				ijk;
    	Element_GlobalIndex		global_I_range = decomp->elementGlobal3DCounts[0];
    	Element_GlobalIndex		global_K_range = decomp->elementGlobal3DCounts[2];

	Element_GlobalIndex		element_gI = _MeshDecomp_Element_LocalToGlobal1D( decomp, element_lI );
        unsigned int			matID=0, tetra_I;	

	RegularMeshUtils_Element_1DTo3D( decomp, element_gI, &ijk[0], &ijk[1], &ijk[2] );

	if( (ijk[0] < (global_I_range-ijk[2])) &&
		(ijk[2] < global_K_range/4) )
		matID=1;
	if((ijk[0]==(global_I_range-ijk[2]-1)) &&
		(ijk[2] < (global_K_range/4)) )
		matID=2;
	if( (ijk[0]==global_I_range/2) &&
		(ijk[2]>=global_K_range/4) )
		matID=2;
	if( ((ijk[0]>=global_I_range/2)&&(ijk[0]<=global_I_range*3/4))
		&&
		(ijk[2]==global_K_range/4) )
		matID=2;

	(*material_I)=matID;

	for( tetra_I = 0; tetra_I < Tetrahedra_Count; tetra_I++ ) {
		element->tetra[tetra_I].material_I=matID;
	}
}
Example #4
0
void SnacViscoPlastic_Constitutive( void* _context, Element_LocalIndex element_lI ) {
	Snac_Context* context = (Snac_Context*)_context;
	Snac_Element* element = Snac_Element_At( context, element_lI );
	SnacViscoPlastic_Element* viscoplasticElement = ExtensionManager_Get( context->mesh->elementExtensionMgr, element, SnacViscoPlastic_ElementHandle );
	SnacTemperature_Element* temperatureElement = ExtensionManager_Get( context->mesh->elementExtensionMgr, element, SnacTemperature_ElementHandle );
	const Snac_Material* material = &context->materialProperty[element->material_I];

	/*ccccc*/
	MeshLayout*			meshLayout = (MeshLayout*)context->meshLayout;
	HexaMD*				decomp = (HexaMD*)meshLayout->decomp;
	IJK				ijk;
	Element_GlobalIndex		element_gI = _MeshDecomp_Element_LocalToGlobal1D( decomp, element_lI );
	EntryPoint* 			temperatureEP;

	RegularMeshUtils_Element_1DTo3D( decomp, element_gI, &ijk[0], &ijk[1], &ijk[2] );
	/*ccccc*/

	temperatureEP = Context_GetEntryPoint( context,	"Snac_EP_LoopElementsEnergy" );

	/* If this is a ViscoPlastic material, calculate its stress. */
	if ( material->rheology & Snac_Material_ViscoPlastic ) {
		Tetrahedra_Index	tetra_I;
		Node_LocalIndex		node_lI;

		/* viscoplastic material properties */
		const double		bulkm = material->lambda + 2.0f * material->mu/3.0f;
		StressTensor*		stress;
		StrainTensor*		strain;
		Strain				plasticStrain;
		double*				viscosity;
		double				straind0,straind1,straind2,stressd0,stressd1,stressd2;
		double				Stress0[3][3];
		double				trace_strain;
		double				trace_stress;
		double				temp;
		double				vic1;
		double				vic2;
		double				VolumicStress;
		double				rviscosity=material->refvisc;
		double				rmu= material->mu;
		double				srJ2;
		double				avgTemp;
		plModel				yieldcriterion=material->yieldcriterion;

		/* For now reference values of viscosity, second invariant of deviatoric */
		/* strain rate and reference temperature  are being hard wired ( these specific */
		/* values are from paper by Hall et. al., EPSL, 2003 */
		double				rstrainrate = material->refsrate;
		double				rTemp = material->reftemp;
		double				H = material->activationE; // kJ/mol
		double				srexponent = material->srexponent;
		double				srexponent1 = material->srexponent1;
		double				srexponent2 = material->srexponent2;
		const double		R=8.31448;  // J/mol/K
		/* elasto-plastic material properties */
		double				cohesion = 0.0f;
		double				frictionAngle = 0.0f;
		double				dilationAngle = 0.0f;
		double				hardening = 0.0f;
		double				tension_cutoff=0.0;
		const double		degrad = PI / 180.0f;
		double				totalVolume=0.0f,depls=0.0f;
		unsigned int		i;
		double				tmp=0.0;
		const double		a1 = material->lambda + 2.0f * material->mu ;
		const double		a2 = material->lambda ;
		int					ind=0;

		int principal_stresses(StressTensor* stress,double sp[],double cn[3][3]);
		int principal_stresses_orig(StressTensor* stress, double sp[3], double cn[3][3]);

		/*    printf("Entered ViscoPlastic update \n"); */

		/* Work out the plastic material properties of this element */
		for( tetra_I = 0; tetra_I < Tetrahedra_Count; tetra_I++ ) {
			double			cn[3][3] = {{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0}};
			double			s[3] = {0.0,0.0,0.0};
			double			alam, dep1, dep2, dep3, depm;
			/*ccccc*/

			stress = &element->tetra[tetra_I].stress;
			strain = &element->tetra[tetra_I].strain;
			plasticStrain = viscoplasticElement->plasticStrain[tetra_I];
			viscosity = &viscoplasticElement->viscosity[tetra_I];

			if( context->computeThermalStress ) {
				(*stress)[0][0] += temperatureElement->thermalStress[tetra_I];
				(*stress)[1][1] += temperatureElement->thermalStress[tetra_I];
				(*stress)[2][2] += temperatureElement->thermalStress[tetra_I];
			}

			/* storing original stress in local array */
			Stress0[0][0] = (*stress)[0][0];
			Stress0[1][1] = (*stress)[1][1];
			Stress0[2][2] = (*stress)[2][2];
			Stress0[0][1] = (*stress)[0][1];
			Stress0[0][2] = (*stress)[0][2];
			Stress0[1][2] = (*stress)[1][2];
			trace_stress = (*stress)[0][0] + (*stress)[1][1] + (*stress)[2][2];
			/*  trace_strain = element->tetra[tetra_I].volume/element->tetra[tetra_I].old_volume-1.0f; */
			trace_strain = (*strain)[0][0] + (*strain)[1][1] + (*strain)[2][2];

			/*     printf(" Trace strain=%g\t Trace Stress=%g\n",trace_strain,trace_stress); */

			/* Deviatoric Stresses and Strains */
			straind0 =  (*strain)[0][0] -  (trace_strain) / 3.0f;
			straind1 =  (*strain)[1][1] -  (trace_strain) / 3.0f;
			straind2 =  (*strain)[2][2] -  (trace_strain) / 3.0f;

			stressd0 =  (*stress)[0][0] -  (trace_stress) / 3.0f;
			stressd1 =  (*stress)[1][1] -  (trace_stress) / 3.0f;
			stressd2 =  (*stress)[2][2] -  (trace_stress) / 3.0f;

			/* compute viscosity and add thermal stress */
			if( temperatureEP ) {

				srJ2 = sqrt(fabs(straind1*straind2+straind2*straind0+straind0*straind1 -(*strain[0][1])*(*strain[0][1])-(*strain[0][2])*(*strain[0][2])-(*strain[1][2])*(*strain[1][2])))/context->dt;
				if(srJ2 == 0.0f) srJ2 = rstrainrate; // temporary. should be vmax/length_scale

				avgTemp=0.0;
				for(node_lI=0; node_lI<4; node_lI++) {
					Snac_Node* contributingNode = Snac_Element_Node_P(
																	  context,
																	  element_lI,
																	  TetraToNode[tetra_I][node_lI] );
					SnacTemperature_Node* temperatureNodeExt = ExtensionManager_Get(
																					context->mesh->nodeExtensionMgr,contributingNode,
																					SnacTemperature_NodeHandle );

					avgTemp += 0.25 * temperatureNodeExt->temperature;
					assert( !isnan(avgTemp) && !isinf(avgTemp) );
				}
				// Hall et. al., 2004, G3
				(*viscosity)= rviscosity*pow((srJ2/rstrainrate),(1./srexponent-1.))
					*exp(H/R*(1./(avgTemp+273.15)-1./(rTemp+273.15)));
				if((*viscosity) < material->vis_min) (*viscosity) = material->vis_min;
				if((*viscosity) > material->vis_max) (*viscosity) = material->vis_max;
				Journal_Firewall(
								 !isnan((*viscosity)) && !isinf((*viscosity)),
								 context->snacError,
								 "rvisc=%e Erattio=%e pow(E)=%e, dT=%e exp=%e\n",
								 rviscosity,
								 (srJ2/rstrainrate),
								 pow((srJ2/rstrainrate),
									 (1./srexponent-1.)),
								 exp(H/R*(1./(avgTemp+273.15)-1./(rTemp+273.15))),
								 (1./(avgTemp+273.15)-1./(rTemp+273.15)) );
#if 0
				// Lavier and Buck, JGR, 2002
				(*viscosity) = pow(rviscosity,-1.0/srexponent1)*pow(srJ2,1.0/srexponent2-1)*exp(H/R/(avgTemp+273.15));
#endif
			}
			else {
				(*viscosity) = rviscosity;
				Journal_Firewall(
								 !isnan((*viscosity)) && !isinf((*viscosity)),
								 context->snacError,
								 "(*viscosity) is nan or inf\n" );
			}

			/* Non dimensional parameters elastic/viscous */
			temp = rmu / (2.0f* (*viscosity)) * context->dt;

			vic1 = 1.0f - temp;
			vic2 = 1.0f / (1.0f + temp);
			/*    printf("temp=%g\t rmu=%g\t viscosity=%g\t\n",temp,rmu,*viscosity); */
			/*                          printf("trace_stress=%g\t trace_strain=%g\t vic1=%g\t vic2=%g\n",trace_stress,trace_strain,vic1,vic2); */
			/* Deviatoric Stress Update */

			stressd0 =  (stressd0 * vic1 + 2.0f * rmu * straind0) * vic2 ;
			stressd1 =  (stressd1 * vic1 + 2.0f * rmu * straind1) * vic2 ;
			stressd2 =  (stressd2 * vic1 + 2.0f * rmu * straind2) * vic2 ;

			(*stress)[0][1] =((*stress)[0][1] * vic1 + 2.0f * rmu * (*strain)[0][1]) * vic2;
			(*stress)[0][2] =((*stress)[0][2] * vic1 + 2.0f * rmu * (*strain)[0][2]) * vic2;
			(*stress)[1][2] =((*stress)[1][2] * vic1 + 2.0f * rmu * (*strain)[1][2]) * vic2;

			/* Isotropic stress is elastic,
			   WARNING:volumic Strain may be better defined as
			   volumique change in the mesh */
			VolumicStress = trace_stress / 3.0f + bulkm * trace_strain;

			(*stress)[0][0] = stressd0 + VolumicStress;
			(*stress)[1][1] = stressd1 + VolumicStress;
			(*stress)[2][2] = stressd2 + VolumicStress;

			principal_stresses(stress,s,cn);

			/* compute friction and dilation angles based on accumulated plastic strain in tetrahedra */
			/* Piece-wise linear softening */
			/* Find current properties from linear interpolation */
#if 0
			/*ccccc*/
			if(material->putSeeds && context->loop <= 1) {
				if(ijk[1] >= decomp->elementGlobal3DCounts[1]-2)
					if(ijk[0] == decomp->elementGlobal3DCounts[0]/2 ) {
						//if(ijk[0] >= 10 && ijk[0] <= 14 ) {
						viscoplasticElement->plasticStrain[tetra_I] = 1.1*material->plstrain[1];
						plasticStrain = viscoplasticElement->plasticStrain[tetra_I];
						fprintf(stderr,"loop=%d ijk=%d %d %d aps=%e plasticE=%e\n",
								context->loop,ijk[0],ijk[1],ijk[2],plasticStrain,
								viscoplasticElement->plasticStrain[tetra_I]);
					}
			}
			/*ccccc*/
#endif
			for( i = 0; i < material->nsegments; i++ ) {
				const double pl1 = material->plstrain[i];
				const double pl2 = material->plstrain[i+1];

				if( plasticStrain >= pl1 && plasticStrain <= pl2 ) {
					const double	tgf = (material->frictionAngle[i+1] - material->frictionAngle[i]) / (pl2 - pl1);
					const double	tgd = (material->dilationAngle[i+1] - material->dilationAngle[i]) / (pl2 - pl1);
					const double	tgc = (material->cohesion[i+1] - material->cohesion[i]) / (pl2 - pl1);

					frictionAngle = material->frictionAngle[i] + tgf * (plasticStrain - pl1);
					dilationAngle = material->dilationAngle[i] + tgd * (plasticStrain - pl1);
					cohesion = material->cohesion[i] + tgc * (plasticStrain - pl1);
					hardening = tgc;
				}
			}

			if( frictionAngle > 0.0f ) {
				tension_cutoff = material->ten_off;
				if( frictionAngle > 0.0) {
					tmp = cohesion / tan( frictionAngle * degrad);
					if(tmp < tension_cutoff) tension_cutoff=tmp;
				}
			}
			else {
				if( plasticStrain < 0.0 ) {
					viscoplasticElement->plasticStrain[tetra_I] = 0.0;
					plasticStrain = viscoplasticElement->plasticStrain[tetra_I];
					fprintf(stderr,"Warning: negative plastic strain. Setting to zero, but check if remesher is on and this happended for an external tet. rank:%d elem:%d tet:%d plasticStrain=%e frictionAngle=%e\n",context->rank,element_lI,tetra_I,plasticStrain,frictionAngle);
					frictionAngle = material->frictionAngle[0];
					dilationAngle = material->dilationAngle[0];
					cohesion = material->cohesion[0];
				}
				else {
					/* frictionAngle < 0.0 violates second law of thermodynamics */
					fprintf(stderr,"Error due to an unknown reason: rank:%d elem:%d tet:%d plasticStrain=%e frictionAngle=%e\n",context->rank,element_lI,tetra_I,plasticStrain,frictionAngle);
					assert(0);
				}
			}

			if( yieldcriterion == mohrcoulomb )
				{

					double sphi = sin( frictionAngle * degrad );
					double spsi = sin( dilationAngle * degrad );
					double anphi = (1.0f + sphi) / (1.0f - sphi);
					double anpsi = (1.0f + spsi) / (1.0f - spsi);
					double fs = s[0] - s[2] * anphi + 2 * cohesion * sqrt( anphi );
					double ft = s[2] - tension_cutoff;
					/* CHECK FOR COMPOSITE YIELD CRITERION  */
					ind=0;
					if( fs < 0.0f || ft > 0.0f ) {
						/*! Failure: shear or tensile */
						double aP = sqrt( 1.0f + anphi * anphi ) + anphi;
						double sP = tension_cutoff * anphi - 2 * cohesion * sqrt( anphi );
						double h = s[2] - tension_cutoff + aP * ( s[0] - sP );

						ind=1;

						if( h < 0.0f ) {
							/* !shear failure  */
							alam = fs / ( a1 - a2 * anpsi + a1 * anphi * anpsi - a2 * anphi + 2.0*sqrt(anphi)*hardening );
							s[0] -= alam * ( a1 - a2 * anpsi );
							s[1] -= alam * a2 * ( 1.0f - anpsi );
							s[2] -= alam * ( a2 - a1 * anpsi );
							dep1 = alam;
							dep2 = 0.0f;
							dep3 = -alam * anpsi;
						}
						else {
							/* tensile failure */
							alam = ft / a1;
							s[0] -= alam * a2;
							s[1] -= alam * a2;
							s[2] -= alam * a1;
							dep1 = 0.0f;
							dep2 = 0.0f;
							dep3 = alam;
						}
					}
					else {
						/* ! no failure - just elastic increment */

						dep1 = 0.0f;
						dep2 = 0.0f;
						dep3 = 0.0f;
					}
					if(ind) {
						unsigned int k,m,n;
						/* Second invariant of accumulated plastic increment  */
						depm = ( dep1 + dep2 + dep3 ) / 3.0f;
						viscoplasticElement->plasticStrain[tetra_I] += sqrt( 0.5f * ((dep1-depm) * (dep1-depm) + (dep2-depm) * (dep2-depm) + (dep3-depm) * (dep3-depm) + depm*depm) );

						/* Stress projection back to euclidean coordinates */
						memset( stress, 0, sizeof((*stress)) );
						/* Resolve back to global axes  */
						for( m = 0; m < 3; m++ ) {
							for( n = m; n < 3; n++ ) {
								for( k = 0; k < 3; k++ ) {
									/* (*stress)[m][n] += cn[k][m] * cn[k][n] * s[k]; */
									(*stress)[m][n] += cn[m][k] * cn[n][k] * s[k];
								}
							}
						}
					}
				}
			else
				Journal_Firewall( (0>1), "In %s: \"mohrcoulomb\" is the only available yield criterion.\n", __func__ );

			/* linear healing: applied whether this tet has yielded or not. 
			   Parameters are hardwired for now, but should be given through an input file. */
			/* viscoplasticElement->plasticStrain[tetra_I] *= (1.0/(1.0+context->dt/1.0e+12)); */
			viscoplasticElement->plasticStrain[tetra_I] *= (1.0/(1.0+context->dt/(ind?1.0e+13:5.0e+11)));

			depls += viscoplasticElement->plasticStrain[tetra_I]*element->tetra[tetra_I].volume;
			totalVolume += element->tetra[tetra_I].volume;
		}
		/* volume-averaged accumulated plastic strain, aps */
		viscoplasticElement->aps = depls/totalVolume;
	}
}
double _GALEDruckerPrager_GetYieldCriterion( 
			void*                            druckerPrager,
			ConstitutiveMatrix*              constitutiveMatrix,
			MaterialPointsSwarm*             materialPointsSwarm,
			Element_LocalIndex               lElement_I,
			MaterialPoint*                   materialPoint,
			Coord                            xi )
{
	GALEDruckerPrager*                    self             = (GALEDruckerPrager*) druckerPrager;
        Dimension_Index                   dim = constitutiveMatrix->dim;
	double                            cohesion;
	double                            cohesionAfterSoftening;
	double                            frictionCoefficient;
	double                            frictionCoefficientAfterSoftening;
	double                            minimumYieldStress;
	double                            minimumViscosity;
	double                            effectiveCohesion;
	double                            effectiveFrictionCoefficient;
	double                            frictionalStrength;
	double                            pressure;
	GALEDruckerPrager_Particle*           particleExt;
        Cell_Index                        cell_I;
        Coord                             coord;
        Element_GlobalIndex	          element_gI = 0;
        unsigned		          inds[3];
        Grid*			          elGrid;
        Bool                              inside_boundary;
        double                            factor;
	
	/* Get Parameters From Rheology */
	cohesion                           = self->cohesion;
	cohesionAfterSoftening             = self->cohesionAfterSoftening;
	frictionCoefficient                = self->frictionCoefficient;
	frictionCoefficientAfterSoftening  = self->frictionCoefficientAfterSoftening;
	minimumYieldStress                 = self->minimumYieldStress;
	minimumViscosity                   = self->minimumViscosity;
	
	particleExt = (GALEDruckerPrager_Particle*)ExtensionManager_Get( materialPointsSwarm->particleExtensionMgr, materialPoint, self->particleExtHandle );

        if( self->pressureField )
          FeVariable_InterpolateWithinElement( self->pressureField, lElement_I, xi, &pressure );
        else {
          SwarmVariable_ValueAt( self->swarmPressure,
                                 constitutiveMatrix->currentParticleIndex,
                                 &pressure );
        }

        cell_I=CellLayout_MapElementIdToCellId(materialPointsSwarm->cellLayout,
                                               lElement_I );
        FeMesh_CoordLocalToGlobal(self->pressureField->feMesh, cell_I, xi, coord);
        if(self->hydrostaticTerm)
          {
            pressure+=HydrostaticTerm_Pressure(self->hydrostaticTerm,coord);
          }

        /* Normally we add the average of the trace of the stress.
           With compressible material, you have to do it.  But with
           stabilized linear pressure elements, the non-zero trace is
           a numerical artifact.  So we do not add it. */

/*   pressure+=self->trace/dim; */

        /* Calculate frictional strength.  We modify the friction and
           cohesion because we have grouped terms from the normal
           stresses and moved it to the yield indicator. */
	

        /* Big song and dance to see if we are at a boundary that we care about */
        elGrid = *(Grid**)ExtensionManager_Get(self->pressureField->feMesh->info,
                                               self->pressureField->feMesh,
self->pressureField->feMesh->elGridId );
  
        element_gI = FeMesh_ElementDomainToGlobal( self->pressureField->feMesh, lElement_I );
        RegularMeshUtils_Element_1DTo3D( self->pressureField->feMesh, element_gI, inds );
  
        inside_boundary=(self->boundaryBottom && inds[1]==0)
          || (self->boundaryTop && inds[1]==elGrid->sizes[1]-1)
          || (self->boundaryLeft && inds[0]==0)
          || (self->boundaryRight && inds[0]==elGrid->sizes[0]-1)
          || (dim==3 && ((self->boundaryBack && inds[2]==0)
                         || (self->boundaryFront && inds[2]==elGrid->sizes[2]-1)));

        effectiveFrictionCoefficient =
          _GALEDruckerPrager_EffectiveFrictionCoefficient( self, materialPoint,
                                                       inside_boundary );
        effectiveCohesion = _GALEDruckerPrager_EffectiveCohesion(self,materialPoint,
                                                             inside_boundary);

  if(dim==2)
    {
      /* effectiveFrictionCoefficient=tan(phi).  If
         factor=sin(atan(1/tan(phi))) =>
         factor=cos(phi)=1/sqrt(1+tan(phi)**2) */
      factor=1/sqrt(1 + effectiveFrictionCoefficient*effectiveFrictionCoefficient);
      frictionalStrength = effectiveFrictionCoefficient*pressure*factor
        + effectiveCohesion*factor;
    }
  else
    {
      double cos_phi, sin_phi;
      /* cos(phi)=1/sqrt(1+tan(phi)**2) */
      cos_phi=
        1/sqrt(1 + effectiveFrictionCoefficient*effectiveFrictionCoefficient);
      sin_phi=effectiveFrictionCoefficient*cos_phi;
      factor=2*cos_phi/(sqrt(3.0)*(3-sin_phi));

      /* The full expression is

         sqrt(J2)=p*2*sin(phi)/(sqrt(3)*(3-sin(phi)))
                  + C*6*cos(phi)/(sqrt(3)*(3-sin(phi)))

         Note the extra factor of 3 for cohesion */

      frictionalStrength = effectiveFrictionCoefficient*factor*pressure
        + effectiveCohesion*3*factor;
    }
  
  /* If the minimumYieldStress is not set, then use the
     effective cohesion.  Maybe it should be the modified
     effective cohesion, though that probably should not matter
     much. */
  minimumYieldStress = self->minimumYieldStress;
  if(minimumYieldStress==0.0)
    minimumYieldStress=effectiveCohesion;
  
  /* Make sure frictionalStrength is above the minimum */
  if ( frictionalStrength < minimumYieldStress*factor) 
    frictionalStrength = minimumYieldStress*factor;

  self->yieldCriterion = frictionalStrength;
  self->curFrictionCoef = effectiveFrictionCoefficient*factor;

  return frictionalStrength;
}
void Spherical_CubedSphereNusselt_Output( UnderworldContext* context ) {
   Spherical_CubedSphereNusselt* 	self  		= NULL;
   Swarm*				swarm		= NULL;
   FeMesh* 				mesh		= NULL;
   ElementType* 			elementType	= NULL;
   Grid*				grid		= NULL;
   double 				avgT, vol;

   self = (Spherical_CubedSphereNusselt*)LiveComponentRegister_Get( context->CF->LCRegister, (Name)Spherical_CubedSphereNusselt_Type );

   FeVariable 	*temperatureGradientsField 	= self->temperatureGradientsField;
   FeVariable 	*temperatureField 		= self->temperatureField;
   FeVariable 	*velocityField 			= self->velocityField;

   swarm = self->gaussSwarm;
   mesh = (FeMesh*)self->mesh;

   assert( self );
   assert( mesh );
   assert( swarm );

   /*
      for each boundary element at the top
         integrate dT/dr &
         integrate T*v_r

      ASSUMPTIONS:
      * uses grid data structure for r,theta mesh (cartesian topology so i can)
   */

   unsigned 		nEls, e_i, cell_i, nPoints, p_i, ijk[3];
   IntegrationPoint*	particle;
   double 		value[3], xyz[3], rtp[3], detJac, dT_dr, factor, vel[3], T, vel_rtp[3];
   double 		gVolume[2], volume[2];
   double 		J_Nu[2], gJ_Nu[2];
   double		rMin			= ((RSGenerator*)mesh->generator)->crdMin[0];
   double		rMax			= ((RSGenerator*)mesh->generator)->crdMin[1];
   double		dxdr[3], r;

   elementType = FeMesh_GetElementType( mesh, 0 ); // assuming all element are the same as el 0

   memset( J_Nu, 0, 2*sizeof(double) );
   memset( volume, 0, 2*sizeof(double) );

   RegularMeshUtils_ErrorCheckAndGetDetails( (Mesh*)mesh, MT_VOLUME, &nEls, &grid );

   for( e_i = 0; e_i < nEls; e_i++ ) {
      // use cartesian grid data structure - can improve later on to make more general
      RegularMeshUtils_Element_1DTo3D( mesh, Mesh_DomainToGlobal( (Mesh*)mesh, MT_VOLUME, e_i ), ijk );

      // if element is on the outer radius boundary
      if( ijk[0] == grid->sizes[0] - 1 ) {
         // get integration number of integration points in cell
         cell_i = CellLayout_MapElementIdToCellId( swarm->cellLayout, e_i );
         nPoints = swarm->cellParticleCountTbl[ cell_i ];

         for( p_i = 0; p_i < nPoints; p_i++ ) {
            // get integration particle
            particle = (IntegrationPoint*) Swarm_ParticleInCellAt( swarm, cell_i, p_i );

            // get temperatureDeriv and xyz
            FeVariable_InterpolateFromMeshLocalCoord( temperatureField, (FeMesh*)mesh, e_i, particle->xi, &T);
            FeVariable_InterpolateFromMeshLocalCoord( velocityField, (FeMesh*)mesh, e_i, particle->xi, vel);
            FeVariable_InterpolateFromMeshLocalCoord( temperatureGradientsField, (FeMesh*)mesh, e_i, particle->xi, value);
            FeMesh_CoordLocalToGlobal( mesh, e_i, particle->xi, xyz );

            Spherical_XYZ2regionalSphere( xyz, rtp );
            Spherical_VectorXYZ2regionalSphere( vel, xyz, vel_rtp );
            detJac = ElementType_JacobianDeterminant( elementType, (FeMesh*)mesh, e_i, particle->xi, 3 );

            r = sqrt( rtp[0]*rtp[0] + rtp[1]*rtp[1] + rtp[2]*rtp[2] );
            dxdr[0] = r/xyz[0];
            dxdr[1] = r/xyz[1];
            dxdr[2] = r/xyz[2];
            // calc dT_dr = dT_dx * dx_dr + dT_dy * dy_dr + dT_dz * dz_dr
            dT_dr = value[0]*dxdr[0] + value[1]*dxdr[1] + value[2]*dxdr[2];

            // add to element integral
            J_Nu[0] += particle->weight * detJac * (dT_dr - T*vel_rtp[0]);
            volume[0] += detJac * particle->weight;
         }
      }
      // if element is on the inner radius boundary
      if( ijk[0] == 0 ) {
         // get integration number of integration points in cell
         cell_i = CellLayout_MapElementIdToCellId( swarm->cellLayout, e_i );
         nPoints = swarm->cellParticleCountTbl[ cell_i ];

         for( p_i = 0; p_i < nPoints; p_i++ ) {
            // get integration particle
            particle = (IntegrationPoint*) Swarm_ParticleInCellAt( swarm, cell_i, p_i );

            // get temperatureDeriv and xyz
            FeVariable_InterpolateFromMeshLocalCoord( temperatureField, (FeMesh*)mesh, e_i, particle->xi, &T);
            FeVariable_InterpolateFromMeshLocalCoord( velocityField, (FeMesh*)mesh, e_i, particle->xi, vel);
            FeVariable_InterpolateFromMeshLocalCoord( temperatureGradientsField, (FeMesh*)mesh, e_i, particle->xi, value);
            FeMesh_CoordLocalToGlobal( mesh, e_i, particle->xi, xyz );

            Spherical_XYZ2regionalSphere( xyz, rtp );
            Spherical_VectorXYZ2regionalSphere( vel, xyz, vel_rtp );
            detJac = ElementType_JacobianDeterminant( elementType, (FeMesh*)mesh, e_i, particle->xi, 3 );

            r = sqrt( rtp[0]*rtp[0] + rtp[1]*rtp[1] + rtp[2]*rtp[2] );
            dxdr[0] = r/xyz[0];
            dxdr[1] = r/xyz[1];
            dxdr[2] = r/xyz[2];
            // calc dT_dr = dT_dx * dx_dr + dT_dy * dy_dr + dT_dz * dz_dr
            dT_dr = value[0]*dxdr[0] + value[1]*dxdr[1] + value[2]*dxdr[2];

            J_Nu[1] += particle->weight * detJac * (dT_dr - T*vel_rtp[0]);
            volume[1] += detJac * particle->weight;
         }
      }
   }

   /* Sum of procs integral */
   (void)MPI_Allreduce( J_Nu, gJ_Nu, 2, MPI_DOUBLE, MPI_SUM, context->communicator );
   (void)MPI_Allreduce( volume, gVolume, 2, MPI_DOUBLE, MPI_SUM, context->communicator );

   // to get horizontally averaged quantities we divide by volume
   gJ_Nu[0] /= gVolume[0];
   gJ_Nu[1] /= gVolume[1];

   // normalise CubedSphereNusselt upper condition - this is for scaling against published results
   // 1.22 and 2.22 are the inner and outer radii for those results
   factor = rMax * log(0.55);
   gJ_Nu[0] = factor *  gJ_Nu[0];

   // normalise CubedSphereNusselt lower condition
   factor = rMin * log(0.55);
   gJ_Nu[1] = factor * gJ_Nu[1];

   avgT = PpcIntegral_Integrate( self->volAvgT );
   vol = PpcIntegral_Integrate( self->vol );

   StgFEM_FrequentOutput_PrintValue( context, avgT/vol );
   StgFEM_FrequentOutput_PrintValue( context, gJ_Nu[0] );
   StgFEM_FrequentOutput_PrintValue( context, gJ_Nu[1] );
}