void _VectorAssemblyTerm_NA__Fn_AssembleElement( void* forceTerm, ForceVector* forceVector, Element_LocalIndex lElement_I, double* elForceVec ) {
   VectorAssemblyTerm_NA__Fn* self = Stg_CheckType( forceTerm, VectorAssemblyTerm_NA__Fn );
   IntegrationPointsSwarm*    swarm = (IntegrationPointsSwarm*)self->integrationSwarm;
   Dimension_Index            dim = forceVector->dim;
   IntegrationPoint*          particle;
   FeMesh*                    mesh;
   double*                    xi;
   Particle_InCellIndex       cParticle_I;
   Particle_InCellIndex       cellParticleCount;
   Element_NodeIndex          nodesPerEl;
   Node_ElementLocalIndex     A;
   ElementType*               elementType;
   Dof_Index                  dofsPerNode, i;
   Cell_Index                 cell_I;
   double                     detJac;
   double                     factor;
   double                     N[27];

   /* Since we are integrating over the velocity mesh - we want the velocity mesh here and not the temperature mesh */
   mesh = forceVector->feVariable->feMesh;

   VectorAssemblyTerm_NA__Fn_cppdata* cppdata = (VectorAssemblyTerm_NA__Fn_cppdata*)self->cppdata;
    
   debug_dynamic_cast<ParticleInCellCoordinate*>(cppdata->input->localCoord())->index() = lElement_I;  // set the elementId as the owning cell for the particleCoord
   cppdata->input->index()   = lElement_I;  // set the elementId for the fem coordinate

   /* Set the element type */
   elementType = FeMesh_GetElementType( mesh, lElement_I );
   nodesPerEl  = elementType->nodeCount;
   

   /* assumes constant number of dofs per element */
   dofsPerNode = forceVector->feVariable->fieldComponentCount;

   cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
   cellParticleCount = swarm->cellParticleCountTbl[ cell_I ];

   for ( cParticle_I = 0 ; cParticle_I < cellParticleCount ; cParticle_I++ ) {
      debug_dynamic_cast<ParticleInCellCoordinate*>(cppdata->input->localCoord())->particle_cellId(cParticle_I);  // set the particleCoord cellId
      particle = (IntegrationPoint*) Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
      xi       = particle->xi;

      /* Calculate Determinant of Jacobian and Shape Functions */
      if (self->geometryMesh)
         detJac = ElementType_JacobianDeterminant( FeMesh_GetElementType( self->geometryMesh, lElement_I ), self->geometryMesh, lElement_I, xi, dim );
      else
         detJac = ElementType_JacobianDeterminant( elementType, mesh, lElement_I, xi, dim );

      ElementType_EvaluateShapeFunctionsAt( elementType, xi, N );

      /* evaluate function */
      const FunctionIO* funcout = debug_dynamic_cast<const FunctionIO*>(cppdata->func(cppdata->input.get()));

      factor = detJac * particle->weight;
      for( A = 0 ; A < nodesPerEl ; A++ )
         for( i = 0 ; i < dofsPerNode ; i++ )
            elForceVec[A * dofsPerNode + i ] += factor * funcout->at<double>(i) * N[A] ;

   }
}
void _MeshParticleLayout_InitialiseParticlesOfCell( void* meshParticleLayout, void* _swarm, Cell_Index cell_I ) {
	MeshParticleLayout*	self = (MeshParticleLayout*)meshParticleLayout;
	Swarm*                      	swarm   		= (Swarm*)_swarm;
	Particle_InCellIndex		particlesThisCell 	= swarm->cellParticleCountTbl[cell_I];
	Particle_InCellIndex		cParticle_I 		= 0;
	GlobalParticle*	        	particle 		= NULL;
	double				        lCoord[3];
	unsigned		         	dim_i;
    unsigned	         	    nDims = Mesh_GetDimSize( self->mesh );


	for( cParticle_I = 0; cParticle_I < particlesThisCell; cParticle_I++ ) {	
		particle = (GlobalParticle*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
		particle->owningCell = cell_I;

        if (self->filltype == 0 ) {
            for( dim_i = 0; dim_i < nDims; dim_i++ )
                lCoord[dim_i] = SobolGenerator_GetNextNumber_WithMinMax( self->sobolGenerator[dim_i], -1.0, +1.0 );
        } else if (self->filltype == 1){
            for( dim_i = 0; dim_i < nDims; dim_i++ )
                lCoord[dim_i] = Swarm_Random_Random_WithMinMax( -1.0, +1.0 );
        } else
            Journal_Firewall(
				NULL,
				NULL,
				"In func %s: Invalid fill type setting.  Must be 0 (space filler), or 1 (random).",
				__func__ );


		FeMesh_CoordLocalToGlobal( self->mesh, cell_I, lCoord, particle->coord );
	}
}
double IntegrateField( FeVariable* tempField, IntegrationPointsSwarm* swarm ) {
	FeMesh*			mesh	= tempField->feMesh;
	unsigned		el_i;
	unsigned		cell_i;
	unsigned		point_i;
	double			lTemp	= 0.0;
	double			gTemp;
	double			temp;
	double			detJac;
	IntegrationPoint*	point;
	ElementType*		elType;
	unsigned		dim	= Mesh_GetDimSize( mesh );
	
	for( el_i = 0; el_i < Mesh_GetLocalSize( mesh, dim ); el_i++ ) {
		cell_i = CellLayout_MapElementIdToCellId( swarm->cellLayout, el_i );
		elType = FeMesh_GetElementType( mesh, el_i );
		
		for( point_i = 0; point_i < swarm->cellParticleCountTbl[cell_i]; point_i++ ) {
			point = (IntegrationPoint*)Swarm_ParticleInCellAt( swarm, cell_i, point_i );

			FeVariable_InterpolateWithinElement( tempField, el_i, point->xi, &temp );
			detJac = ElementType_JacobianDeterminant( elType, mesh, el_i, point->xi, dim );

			lTemp += detJac * point->weight * temp;
		}
	}

	MPI_Allreduce( &lTemp, &gTemp, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );

	return gTemp;
}
void _BuoyancyForceTermPpc_AssembleElement( void* _self, ForceVector* forceVector, Element_LocalIndex lElement_I, double* elForceVec ) {
    BuoyancyForceTermPpc* self = (BuoyancyForceTermPpc*) _self;
    IntegrationPoint* particle;
    Particle_InCellIndex cParticle_I;
    Particle_InCellIndex cellParticleCount;
    Element_NodeIndex elementNodeCount;
    Dimension_Index dim = forceVector->dim;
    IntegrationPointsSwarm* swarm = (IntegrationPointsSwarm*)self->integrationSwarm;
    FeMesh* mesh = forceVector->feVariable->feMesh;
    Node_ElementLocalIndex eNode_I;
    unsigned int dim_I;
    Cell_Index cell_I;
    ElementType* elementType;
    Dof_Index nodeDofCount;
    double gravity[3], factor, density;
    double detJac = 0.0;
    double Ni[27];
    double* xi;
    int err;

    elementType = FeMesh_GetElementType( mesh, lElement_I );
    elementNodeCount = elementType->nodeCount;
    nodeDofCount = dim;
    cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
    cellParticleCount = swarm->cellParticleCountTbl[cell_I];

    for( cParticle_I = 0 ; cParticle_I < cellParticleCount ; cParticle_I++ ) {
        particle = (IntegrationPoint*) Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
        xi = particle->xi;

        detJac = ElementType_JacobianDeterminant( elementType, mesh, lElement_I, xi, dim );
        ElementType_EvaluateShapeFunctionsAt( elementType, xi, Ni );

        /* Density */
        err = PpcManager_Get( self->manager, lElement_I, particle, self->density_id, &density );

        Journal_Firewall( !err,
                          Journal_Register( Error_Type, (Name)BuoyancyForceTermPpc_Type  ),
                          "%d not found at the PpcManager\n", self->density_id );

        Journal_Firewall( !isnan( density ),
                          Journal_Register( Error_Type, (Name)BuoyancyForceTermPpc_Type  ),
                          "Density at integration point %i of element %i is nan\n", cParticle_I, lElement_I );

        /* Gravity */
        PpcManager_GetGravity( self->manager, lElement_I, particle, gravity );

        /* Apply force in the correct direction */
        for( eNode_I = 0 ; eNode_I < elementNodeCount; eNode_I++ ) {
            for( dim_I = 0 ; dim_I < dim ; dim_I++ ) {
                factor = detJac * particle->weight * density * gravity[dim_I];
                elForceVec[ eNode_I * nodeDofCount + dim_I ] += -1.0 * factor * Ni[ eNode_I ] ;
            }
        }
    }
}
Esempio n. 5
0
void _MeshParticleLayout_InitialiseParticlesOfCell( void* meshParticleLayout, void* _swarm, Cell_Index cell_I ) {
	MeshParticleLayout*	self = (MeshParticleLayout*)meshParticleLayout;
	Swarm*              	swarm = (Swarm*)_swarm;
	Coord               	min = {-1.0, -1.0, -1.0};
	Coord               	max = {1.0, 1.0, 1.0};
	Coord			localCoord;
	double			basis[8];
	unsigned		nDims = self->mesh->nSpaceDims;
	Coord*			nodeCoords = self->mesh->nodeCoord;
	unsigned		nNodes, *incNodes;
	Particle_InCellIndex	particlesThisCell = swarm->cellParticleCountTbl[cell_I];
	Particle_InCellIndex	cParticle_I = 0;
	GlobalParticle*	        particle = NULL;
	unsigned		d_i, n_i;

	assert( nDims == 2 || nDims == 3 );

	nNodes = self->mesh->elementNodeCountTbl[cell_I];
	incNodes = self->mesh->elementNodeTbl[cell_I];

	for ( cParticle_I = 0; cParticle_I < particlesThisCell; cParticle_I++ ) {	
		particle = (GlobalParticle*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
		particle->owningCell = cell_I;
		
		for ( d_i = 0; d_i < nDims; d_i++ ) {
			localCoord[d_i] = Swarm_Random_Random_WithMinMax( min[d_i], max[d_i] );
		}

		/* Convert the coordinate to global. Assumes quad or hex mesh. */
		if( nDims == 2 ) {
			basis[0] = 0.25 * (1.0 - localCoord[0]) * (1.0 - localCoord[1]);
			basis[1] = 0.25 * (1.0 + localCoord[0]) * (1.0 - localCoord[1]);
			basis[3] = 0.25 * (1.0 - localCoord[0]) * (1.0 + localCoord[1]);
			basis[2] = 0.25 * (1.0 + localCoord[0]) * (1.0 + localCoord[1]);
		}
		else {
			basis[0] = 0.125 * (1.0 - localCoord[0]) * (1.0 - localCoord[1]) * (1.0 - localCoord[2]);
			basis[1] = 0.125 * (1.0 + localCoord[0]) * (1.0 - localCoord[1]) * (1.0 - localCoord[2]);
			basis[3] = 0.125 * (1.0 - localCoord[0]) * (1.0 + localCoord[1]) * (1.0 - localCoord[2]);
			basis[2] = 0.125 * (1.0 + localCoord[0]) * (1.0 + localCoord[1]) * (1.0 - localCoord[2]);
			basis[4] = 0.125 * (1.0 - localCoord[0]) * (1.0 - localCoord[1]) * (1.0 + localCoord[2]);
			basis[5] = 0.125 * (1.0 + localCoord[0]) * (1.0 - localCoord[1]) * (1.0 + localCoord[2]);
			basis[7] = 0.125 * (1.0 - localCoord[0]) * (1.0 + localCoord[1]) * (1.0 + localCoord[2]);
			basis[6] = 0.125 * (1.0 + localCoord[0]) * (1.0 + localCoord[1]) * (1.0 + localCoord[2]);
		}

		memset( particle->coord, 0, sizeof(double) * nDims );
		for( d_i = 0; d_i < nDims; d_i++ ) {
			for( n_i = 0; n_i < nNodes; n_i++ ) {
				particle->coord[d_i] += basis[n_i] * nodeCoords[incNodes[n_i]][d_i];
			}
		}
	}
}
double SemiLagrangianIntegratorSuite_EvaluateError( FeVariable* phiField, FeVariable* phiOldField, Swarm* gaussSwarm ) {
   FeMesh*			feMesh		= phiField->feMesh;
   GaussParticleLayout*	particleLayout 	= (GaussParticleLayout*)gaussSwarm->particleLayout;
   Index			lElement_I, lCell_I;
   unsigned		nDims		= Mesh_GetDimSize( feMesh );
   unsigned		numMeshElements	= Mesh_GetLocalSize( feMesh, nDims );
   double			elementError;
   double			lErrorSq	= 0.0;
   double			lAnalyticSq 	= 0.0;
   double			gErrorSq, gAnalyticSq, gErrorNorm;
   IntegrationPoint*	gaussPoint;
   unsigned		gaussPoint_I, numGaussPoints;
   double			initialValue, finalValue;
   double			elErrorSq, elAnalyticSq;
   ElementType*		elementType;
   double			detJac;

   for( lElement_I = 0; lElement_I < numMeshElements; lElement_I++ ) {
      lCell_I = CellLayout_MapElementIdToCellId( gaussSwarm->cellLayout, lElement_I );
      numGaussPoints = _GaussParticleLayout_InitialCount( particleLayout, NULL, lCell_I );

      elementType = FeMesh_GetElementType( feMesh, lElement_I );

      elErrorSq = 0.0;
      elAnalyticSq = 0.0;

      for( gaussPoint_I = 0; gaussPoint_I < numGaussPoints; gaussPoint_I++ ) {
         gaussPoint = (IntegrationPoint*) Swarm_ParticleInCellAt( gaussSwarm, lCell_I, gaussPoint_I );
         FeVariable_InterpolateWithinElement( phiOldField, lElement_I, gaussPoint->xi, &initialValue );
         FeVariable_InterpolateWithinElement( phiField, lElement_I, gaussPoint->xi, &finalValue );

         detJac = ElementType_JacobianDeterminant( elementType, feMesh, lElement_I, gaussPoint->xi, nDims );

         elErrorSq += ( finalValue - initialValue ) * ( finalValue - initialValue ) * gaussPoint->weight * detJac;
         elAnalyticSq += ( initialValue * initialValue ) * gaussPoint->weight * detJac;
      }

      elementError = sqrt( elErrorSq ) / sqrt( elAnalyticSq );

      lErrorSq += elErrorSq;
      lAnalyticSq += elAnalyticSq;
   }

   MPI_Allreduce( &lErrorSq, &gErrorSq, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );
   MPI_Allreduce( &lAnalyticSq, &gAnalyticSq, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );

   gErrorNorm = sqrt( gErrorSq ) / sqrt( gAnalyticSq );

   return gErrorNorm;
}
void StGermain_VaryingCornerAttractors_UpdatePositions( DomainContext* context ) {
	Cell_LocalIndex			lCell_I;
	Particle_InCellIndex		cParticle_I;
	Particle* 	        	currParticle;
	Index				dim_I;
	Swarm*                          swarm = (Swarm*) LiveComponentRegister_Get( context->CF->LCRegister, (Name)"swarm"  );
	Coord                           attractorPoint;
	BlockGeometry*                  blockGeometry;
	Stream*                         stream = Journal_Register( Debug_Type, (Name)"particleUpdate" );
	unsigned int                    movementSpeedDivisor = 10;
	int                             movementSign = 1;
	unsigned int                    cornerPeriod = 10;
	unsigned int                    numCorners = (swarm->dim-1 )*4;
	unsigned int                    explosionPeriod = numCorners*cornerPeriod;
	Coord                           cornerCoords[8];
	int                             modValue = 0;
	int                             cornerIndex = 0;

	Stream_SetPrintingRank( stream, Dictionary_GetUnsignedInt_WithDefault( context->dictionary, "procToWatch", 0 ) );
	
	blockGeometry = (BlockGeometry*) LiveComponentRegister_Get( context->CF->LCRegister, (Name)"geometry" );

		/* Bottom left corner */
	cornerCoords[0][I_AXIS] = blockGeometry->min[I_AXIS];
	cornerCoords[0][J_AXIS] = blockGeometry->min[J_AXIS];
	cornerCoords[0][K_AXIS] = blockGeometry->min[K_AXIS];
	/* Bottom right corner */
	cornerCoords[1][I_AXIS] = blockGeometry->max[I_AXIS];
	cornerCoords[1][J_AXIS] = blockGeometry->min[J_AXIS];
	cornerCoords[1][K_AXIS] = blockGeometry->min[K_AXIS];
	/* Top right corner */
	cornerCoords[2][I_AXIS] = blockGeometry->max[I_AXIS];
	cornerCoords[2][J_AXIS] = blockGeometry->max[J_AXIS];
	cornerCoords[2][K_AXIS] = blockGeometry->min[K_AXIS];
	/* Top left corner */
	cornerCoords[3][I_AXIS] = blockGeometry->min[I_AXIS];
	cornerCoords[3][J_AXIS] = blockGeometry->max[J_AXIS];
	cornerCoords[3][K_AXIS] = blockGeometry->min[K_AXIS];
	/* Bottom left corner */
	cornerCoords[4][I_AXIS] = blockGeometry->min[I_AXIS];
	cornerCoords[4][J_AXIS] = blockGeometry->max[J_AXIS];
	cornerCoords[4][K_AXIS] = blockGeometry->max[K_AXIS];
	/* Bottom right corner */
	cornerCoords[5][I_AXIS] = blockGeometry->min[I_AXIS];
	cornerCoords[5][J_AXIS] = blockGeometry->min[J_AXIS];
	cornerCoords[5][K_AXIS] = blockGeometry->max[K_AXIS];
	/* Top right corner */
	cornerCoords[6][I_AXIS] = blockGeometry->max[I_AXIS];
	cornerCoords[6][J_AXIS] = blockGeometry->min[J_AXIS];
	cornerCoords[6][K_AXIS] = blockGeometry->max[K_AXIS];
	/* Top left corner */
	cornerCoords[7][I_AXIS] = blockGeometry->max[I_AXIS];
	cornerCoords[7][J_AXIS] = blockGeometry->max[J_AXIS];
	cornerCoords[7][K_AXIS] = blockGeometry->max[K_AXIS];

	/* calculate which corner */
	modValue = (context->timeStep - 1) % (numCorners * cornerPeriod );
	cornerIndex = modValue / cornerPeriod;
	memcpy( attractorPoint, cornerCoords[cornerIndex], 3 * sizeof(double) );
	Journal_Printf( stream, "Calculated attractor point is at (%f,%f,%f):\n", attractorPoint[0], attractorPoint[1], attractorPoint[2] );
	
	/* Can't really explode in this test as particles go out of box */
	#if 0
	/* Now decide if we are attracting or repelling */
	if ( ( ( (context->timeStep - 1) / explosionPeriod ) % 2 ) == 0 ) {
		Journal_Printf( stream, "Timestep %d - Implosive mode\n", context->timeStep );
		movementSign = 1;
	}
	else {
		Journal_Printf( stream, "Timestep %d - Explosive mode\n", context->timeStep );
		movementSign = -1;
	}	
	#endif

	
	for ( lCell_I=0; lCell_I < swarm->cellLocalCount; lCell_I++ ) {
		Journal_Printf( stream, "\tUpdating Particles positions in local cell %d:\n", lCell_I );
		for ( cParticle_I=0; cParticle_I < swarm->cellParticleCountTbl[lCell_I]; cParticle_I++ ) {
			Coord movementVector = {0,0,0};
			Coord newParticleCoord = {0,0,0};
			Coord* oldCoord;

			currParticle = (Particle*)Swarm_ParticleInCellAt( swarm, lCell_I, cParticle_I );
			oldCoord = &currParticle->coord;
			Journal_Printf( stream, "\t\tUpdating particleInCell %d:\n", cParticle_I );

			for ( dim_I=0; dim_I < 3; dim_I++ ) {
				movementVector[dim_I] = ( attractorPoint[dim_I] - (*oldCoord)[dim_I] ) /
					movementSpeedDivisor;
				movementVector[dim_I] *= movementSign;	
				if ( movementSign == -1 ) {
					movementVector[dim_I] *= (float)movementSpeedDivisor / (movementSpeedDivisor-1); 
				}
				newParticleCoord[dim_I] = (*oldCoord)[dim_I] + movementVector[dim_I];
			}
			memcpy( currParticle->velocity, movementVector, 3*sizeof(double) ); 

			Journal_Printf( stream, "\t\tChanging its coords from (%f,%f,%f) to (%f,%f,%f):\n",
				(*oldCoord)[0], (*oldCoord)[1], (*oldCoord)[2],
				newParticleCoord[0], newParticleCoord[1], newParticleCoord[2] );

			for ( dim_I=0; dim_I < 3; dim_I++ ) {
				currParticle->coord[dim_I] = newParticleCoord[dim_I];
			}
		}
	}

	Swarm_UpdateAllParticleOwners( swarm );
}
/** AdvectionDiffusion_UpwindDiffusivity - See Brooks, Hughes 1982 Section 3.3 
 * All equations refer to this paper if not otherwise indicated */
double SUPGAdvDiffTermPpc_UpwindDiffusivity( 
   SUPGAdvDiffTermPpc*    self, 
   AdvectionDiffusionSLE* sle, 
   Swarm*                 swarm, 
   FeMesh*                mesh, 
   Element_LocalIndex     lElement_I, 
   Dimension_Index        dim )
{
   FeVariable*          velocityField = self->velocityField;
   Coord                xiElementCentre = {0.0,0.0,0.0};
   double               xiUpwind;
   double               velocityCentre[3];
   double               pecletNumber;
   double               lengthScale;
   double               upwindDiffusivity;
   Dimension_Index      dim_I;
   double*              leastCoord;
   double*              greatestCoord;
   Node_LocalIndex      nodeIndex_LeastValues, nodeIndex_GreatestValues;
   unsigned             nInc, *inc;
   IArray*              incArray;
   Cell_Index           cell_I;
   IntegrationPoint*    particle;
   Particle_Index       lParticle_I;
   double               averageDiffusivity;
   Particle_InCellIndex cParticle_I;
   Particle_InCellIndex particleCount;
   int                  err;
   double               diffusivity;

   
   /* Compute the average diffusivity */
   /* Find Number of Particles in Element */
   cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
   particleCount = swarm->cellParticleCountTbl[ cell_I ];

   /* Average diffusivity for element */
   averageDiffusivity = 0.0;
   for ( cParticle_I = 0 ; cParticle_I < particleCount ; cParticle_I++ ) {
      particle = (IntegrationPoint*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
      err = PpcManager_Get( self->mgr, lElement_I, particle, self->diffusivityLabel, &diffusivity );
      assert(!err);
      averageDiffusivity += diffusivity;
   }
   averageDiffusivity /= (double)particleCount;
   
   if (sle->maxDiffusivity < averageDiffusivity)
      sle->maxDiffusivity = averageDiffusivity;
   
   /* Change Diffusivity if it is too small */
   if ( averageDiffusivity < SUPG_MIN_DIFFUSIVITY ) 
      averageDiffusivity = SUPG_MIN_DIFFUSIVITY;
   
   /* Calculate Velocity At Middle of Element - See Eq. 3.3.6 */
   FeVariable_InterpolateFromMeshLocalCoord( velocityField, mesh, lElement_I, xiElementCentre, velocityCentre );
   
   /* Calculate Length Scales - See Fig 3.4 - ASSUMES BOX MESH TODO - fix */
   incArray = self->incarray;
   FeMesh_GetElementNodes( mesh, lElement_I, incArray );
   nInc = IArray_GetSize( incArray );
   inc = IArray_GetPtr( incArray );
   
   nodeIndex_LeastValues = inc[0];
   nodeIndex_GreatestValues = (dim == 2) ? inc[3] : (dim == 3) ? inc[7] : inc[1];
   leastCoord = Mesh_GetVertex( mesh, nodeIndex_LeastValues );
   greatestCoord = Mesh_GetVertex( mesh, nodeIndex_GreatestValues );
   
   upwindDiffusivity = 0.0;
   for ( dim_I = 0 ; dim_I < dim ; dim_I++ ) {
      lengthScale = greatestCoord[ dim_I ] - leastCoord[ dim_I ];
      
      /* Calculate Peclet Number (alpha) - See Eq. 3.3.5 */
      pecletNumber = velocityCentre[ dim_I ] * lengthScale / (2.0 * averageDiffusivity);
      
      /* Calculate Upwind Local Coordinate - See Eq. 3.3.4 and (2.4.2, 3.3.1 and 3.3.2) */
      xiUpwind = self->_upwindParam( self, pecletNumber );
      
      /* Calculate Upwind Thermal Diffusivity - See Eq. 3.3.3  */
      upwindDiffusivity += xiUpwind * velocityCentre[ dim_I ] * lengthScale;
   }
   upwindDiffusivity *= ISQRT15; /* See Eq. 3.3.11 */
   
   return upwindDiffusivity;
}
Esempio n. 9
0
void StGermain_Bouncer_UpdatePositions( DomainContext* context ) {
	Cell_LocalIndex			lCell_I;
	Particle_InCellIndex		cParticle_I;
	Particle* 	        	currParticle;
	Index				dim_I;
	Swarm*                          swarm = (Swarm*) LiveComponentRegister_Get( context->CF->LCRegister, (Name)"swarm"  );
	BlockGeometry*                  blockGeom;
	Stream*                         stream = Journal_Register( Debug_Type, (Name)"particleUpdate"  );
	unsigned int                    movementSpeedDivisor = 5;
	Particle_Index                  lParticle_I = 0;

	Stream_SetPrintingRank( stream, Dictionary_GetUnsignedInt_WithDefault( context->dictionary, "procToWatch", 0 ) );
	
	blockGeom = (BlockGeometry*) LiveComponentRegister_Get( context->CF->LCRegister, (Name)"geometry" );

	if ( context->timeStep == 1  ) {
		/* for each particle, set a random velocity */
		for ( lParticle_I=0; lParticle_I < swarm->particleLocalCount; lParticle_I++ ) {
			currParticle = (Particle*)Swarm_ParticleAt( swarm, lParticle_I );
			for ( dim_I=0; dim_I < 3; dim_I++ ) {
				currParticle->velocity[dim_I] = ((double) ( rand() - (double)(RAND_MAX)/2 )) / RAND_MAX * 0.1;
			}	
		}
	}
	
	for ( lCell_I=0; lCell_I < swarm->cellLocalCount; lCell_I++ ) {
		Journal_Printf( stream, "\tUpdating Particles positions in local cell %d:\n", lCell_I );
		for ( cParticle_I=0; cParticle_I < swarm->cellParticleCountTbl[lCell_I]; cParticle_I++ ) {
			Coord movementVector = {0,0,0};
			Coord newParticleCoord = {0,0,0};
			Coord* oldCoord;

			currParticle = (Particle*)Swarm_ParticleInCellAt( swarm, lCell_I, cParticle_I );
			oldCoord = &currParticle->coord;
			Journal_Printf( stream, "\t\tUpdating particleInCell %d:\n", cParticle_I );

			for ( dim_I=0; dim_I < 3; dim_I++ ) {
				movementVector[dim_I] = currParticle->velocity[dim_I] / movementSpeedDivisor;
				if ( ( currParticle->velocity[dim_I] < 0 ) 
					&& ( fabs(currParticle->velocity[dim_I] ) > ((*oldCoord)[dim_I] - blockGeom->min[dim_I]) ) )
				{
					Journal_Printf( stream, "\t\tFlipping vel in %d dir\n", dim_I );
					movementVector[dim_I] *= -1;
					currParticle->velocity[dim_I] *= -1;
				}	
				if ( ( currParticle->velocity[dim_I] > 0 ) 
					&& ( fabs(currParticle->velocity[dim_I] ) > ( blockGeom->max[dim_I] - (*oldCoord)[dim_I] ) ) )
				{
					Journal_Printf( stream, "\t\tFlipping vel in %d dir\n", dim_I );
					movementVector[dim_I] *= -1;
					currParticle->velocity[dim_I] *= -1;
				}	
				
				newParticleCoord[dim_I] = (*oldCoord)[dim_I] + movementVector[dim_I];
			}

			Journal_Printf( stream, "\t\tChanging its coords from (%f,%f,%f) to (%f,%f,%f):\n",
				(*oldCoord)[0], (*oldCoord)[1], (*oldCoord)[2],
				newParticleCoord[0], newParticleCoord[1], newParticleCoord[2] );

			for ( dim_I=0; dim_I < 3; dim_I++ ) {
				currParticle->coord[dim_I] = newParticleCoord[dim_I];
			}
		}
	}

	Swarm_UpdateAllParticleOwners( swarm );
}
Esempio n. 10
0
void lecode_tools_Isostasy_AverageBody(lecode_tools_Isostasy *self,
                                       double** _avg_density, double** _rho_zero_density,
                                       double** _phi)
{
   FeMesh *mesh;
   ElementType *el_type;
   IntegrationPointsSwarm *swarm;
   double *local_density, *global_density;
   double *local_vol, *global_vol;
   double *local_rho_zero_vol, *global_rho_zero_vol;
   double *local_rho_zero_density, *global_rho_zero_density;
   double *local_phi, *global_phi, temp, tempDot;
   int cell, num_particles, num_dims, num_els;
   IntegrationPoint *particle;
   double jac_det;
   double density, alpha, densityFinal;
   Material *mat;
   Bool oneToMany;
   Grid* elGrid;
   int elInds[3], arraySize, arrayPos;
   int ii, jj;

   mesh = self->mesh;
   elGrid = *Mesh_GetExtension( mesh, Grid**,  mesh->elGridId );
   num_dims = Mesh_GetDimSize(mesh);
   swarm = self->swarm;
   num_els = FeMesh_GetElementLocalSize(mesh);

   arraySize=0;
   if ( num_dims == 2 ) arraySize = elGrid->sizes[0];
   else if ( num_dims == 3 ) arraySize = elGrid->sizes[0]*elGrid->sizes[self->zontalAxis];
   else assert(0);

   /* Allocate for the column values. */
   local_vol = (double*)malloc( arraySize*sizeof(double) );
   memset( local_vol, 0, arraySize*sizeof(double) );
   local_density = (double*)malloc( arraySize*sizeof(double) );
   memset( local_density, 0, arraySize*sizeof(double) );
   local_rho_zero_vol = (double*)malloc( arraySize*sizeof(double) );
   memset( local_rho_zero_vol, 0, arraySize*sizeof(double) );
   local_rho_zero_density = (double*)malloc( arraySize*sizeof(double) );
   memset( local_rho_zero_density, 0, arraySize*sizeof(double) );
   local_phi = (double*)malloc( arraySize*sizeof(double) );
   memset( local_phi, 0, arraySize*sizeof(double) );

   /* Initialise temperature. */
   temp = 0.0;

   oneToMany = Stg_Class_IsInstance(swarm->mapper, OneToManyMapper_Type);

   for (ii = 0; ii < num_els; ii++)
   {

      /* Make sure the element is beneath the surface. */
      Grid_Lift( elGrid, FeMesh_ElementDomainToGlobal( mesh, ii ), elInds );
      if ( self->surfaceIdx != -1 && elInds[self->vertAxis] >= self->surfaceIdx )
         continue;

      el_type = FeMesh_GetElementType(mesh, ii);
      cell = CellLayout_MapElementIdToCellId(swarm->cellLayout, ii);
      num_particles = swarm->cellParticleCountTbl[cell];

      for (jj = 0; jj < num_particles; jj++)
      {

         particle = (IntegrationPoint*)Swarm_ParticleInCellAt(swarm, cell, jj);
         jac_det = ElementType_JacobianDeterminant(el_type, mesh, ii, particle->xi, num_dims);

         if(!self->ppcManager){
            density = IntegrationPointMapper_GetDoubleFromMaterial(
                         swarm->mapper, particle, self->buoyancy->materialExtHandle,
                         offsetof(BuoyancyForceTerm_MaterialExt, density) );
            alpha = IntegrationPointMapper_GetDoubleFromMaterial(
                       swarm->mapper, particle, self->buoyancy->materialExtHandle,
                       offsetof(BuoyancyForceTerm_MaterialExt, alpha) );

            if (self->tempField)
            {
               FeVariable_InterpolateFromMeshLocalCoord(self->tempField, self->tempField->feMesh,
                     ii, particle->xi, &temp);
               FeVariable_InterpolateFromMeshLocalCoord(self->tempDotField, self->tempDotField->feMesh,
                     ii, particle->xi, &tempDot);
            }

            densityFinal = density*(1.0 - alpha*temp);

         } else {
            int err;
            /* Density */
            err = PpcManager_Get( self->ppcManager, ii, particle, self->densityID, &densityFinal );
            assert(!err);
         }

         arrayPos = elInds[0];
         if ( num_dims == 3 ) arrayPos += elInds[self->zontalAxis]*elGrid->sizes[0];

         local_vol[arrayPos] += particle->weight*jac_det;
         local_density[arrayPos] += particle->weight*jac_det*densityFinal;

         if (!oneToMany)
         {
            mat = IntegrationPointsSwarm_GetMaterialOn(swarm, particle);
            if (mat->index == self->rho_zero_mat->index)
            {
               local_rho_zero_vol[arrayPos] += particle->weight*jac_det;
               local_rho_zero_density[arrayPos] += particle->weight*jac_det*densityFinal;
            }
         }
         else
         {
            OneToManyRef *ref;
            int cnt;
            int kk;

            ref = OneToManyMapper_GetMaterialRef(swarm->mapper, particle);
            cnt = 0;
            for (kk = 0; kk < ref->numParticles; kk++)
            {
               mat = MaterialPointsSwarm_GetMaterialAt(((OneToManyMapper*)swarm->mapper)->materialSwarm, ref->particleInds[kk]);
               if (mat->index == self->rho_zero_mat->index)
                  cnt++;
            }

            if (2*cnt > ref->numParticles)
            {
               local_rho_zero_vol[arrayPos] += particle->weight*jac_det;
               local_rho_zero_density[arrayPos] += particle->weight*jac_det*densityFinal;
            }
         }

         if (_phi)
         {
            local_phi[arrayPos] += particle->weight*jac_det*(-density*alpha*tempDot);
         }
      }
   }

   /* Allocate for the global column values. */
   global_vol = (double*)malloc( arraySize*sizeof(double) );
   global_density = (double*)malloc( arraySize*sizeof(double) );
   global_rho_zero_vol = (double*)malloc( arraySize*sizeof(double) );
   global_rho_zero_density = (double*)malloc( arraySize*sizeof(double) );
   global_phi = (double*)malloc( arraySize*sizeof(double) );

   MPI_Allreduce(local_vol, global_vol, arraySize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
   MPI_Allreduce(local_density, global_density, arraySize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
   MPI_Allreduce(local_rho_zero_vol, global_rho_zero_vol, arraySize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
   MPI_Allreduce(local_rho_zero_density, global_rho_zero_density, arraySize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
   if (_phi)
      MPI_Allreduce(local_phi, global_phi, arraySize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

   free( local_vol );
   free( local_density );
   free( local_rho_zero_vol );
   free( local_rho_zero_density );
   free( local_phi );

   if ( self->avg )
   {
      for ( ii = 1; ii < arraySize; ii++ )
      {
         global_vol[0] += global_vol[ii];
         global_density[0] += global_density[ii];
         global_rho_zero_vol[0] += global_rho_zero_vol[ii];
         global_rho_zero_density[0] += global_rho_zero_density[ii];
         if ( _phi )
            global_phi[0] += global_phi[ii];
      }
   }

   /* Calculate results. */
   *_avg_density = (double*)malloc( arraySize*sizeof(double) );
   *_rho_zero_density = (double*)malloc( arraySize*sizeof(double) );
   if (_phi)
      *_phi = (double*)malloc( arraySize*sizeof(double) );
   for ( ii = 0; ii < arraySize; ii++ )
   {
      (*_avg_density)[ii] = (global_vol[ii] > 1e-7) ? global_density[ii]/global_vol[ii] : 0.0;
      (*_rho_zero_density)[ii] = (global_rho_zero_vol[ii] > 1e-7) ? global_rho_zero_density[ii]/global_rho_zero_vol[ii] : 0.0;
      if (_phi)
         (*_phi)[ii] = (global_vol[ii] > 1e-7) ? global_phi[ii]/global_vol[ii] : 0.0;
      if ( self->avg )
         break;
   }

   /*
       printf("Global mean density: %g\n", (*_avg_density)[0]);
       printf("Global mean rho_0 density: %g\n", (*_rho_zero_density)[0]);
       printf("Global phi/vol: %g\n", (*_phi)[0]);
   */

   free( global_vol );
   free( global_density );
   free( global_rho_zero_vol );
   free( global_rho_zero_density );
   free( global_phi );
}
void StGermain_SingleAttractor_UpdatePositions( DomainContext* context ) {
	Cell_LocalIndex			lCell_I;
	Particle_InCellIndex		cParticle_I;
	Particle* 	        	currParticle;
	Index				dim_I;
	Swarm*                          swarm = (Swarm*) LiveComponentRegister_Get( context->CF->LCRegister, (Name)"swarm"  );
	Coord                           attractorPoint;
	Mesh*				mesh;
	Stream*                         stream = Journal_Register( Info_Type, (Name)"particleUpdate"  );
	unsigned int                    movementSpeedDivisor = 0;
	int                             movementSign = 1;
	unsigned int                    explosionPeriod = 20;
	double				minCrd[3], maxCrd[3];

	Stream_SetPrintingRank( stream, Dictionary_GetUnsignedInt_WithDefault( context->dictionary, "procToWatch", 0 ) );
	movementSpeedDivisor = Dictionary_GetDouble_WithDefault( context->dictionary, (Dictionary_Entry_Key)"movementSpeedDivisor", 10 );
	
	mesh = (Mesh* )LiveComponentRegister_Get( context->CF->LCRegister, (Name)"mesh-linear"  );
	Mesh_GetGlobalCoordRange( mesh, minCrd, maxCrd );
	for ( dim_I=0; dim_I < 3; dim_I++ ) {
		attractorPoint[dim_I] = (maxCrd[dim_I] - minCrd[dim_I]) / 3;
	}
	Journal_Printf( stream, "Calculated attractor point is at (%f,%f,%f):\n", attractorPoint[0], attractorPoint[1], attractorPoint[2] );
	
	/* Now decide if we are attracting or repelling */
	if ( ( ( (context->timeStep - 1) / explosionPeriod ) % 2 ) == 0 ) {
		Journal_Printf( stream, "Timestep %d - Implosive mode\n", context->timeStep );
		movementSign = 1;
	}
	else {
		Journal_Printf( stream, "Timestep %d - Explosive mode\n", context->timeStep );
		movementSign = -1;
	}	

	
	for ( lCell_I=0; lCell_I < swarm->cellLocalCount; lCell_I++ ) {
		Journal_Printf( stream, "\tUpdating Particles positions in local cell %d:\n", lCell_I );
		for ( cParticle_I=0; cParticle_I < swarm->cellParticleCountTbl[lCell_I]; cParticle_I++ ) {
			Coord movementVector = {0,0,0};
			Coord newParticleCoord = {0,0,0};
			Coord* oldCoord;

			currParticle = (Particle*)Swarm_ParticleInCellAt( swarm, lCell_I, cParticle_I );
			oldCoord = &currParticle->coord;
			Journal_Printf( stream, "\t\tUpdating particleInCell %d:\n", cParticle_I );

			for ( dim_I=0; dim_I < 3; dim_I++ ) {
				movementVector[dim_I] = ( attractorPoint[dim_I] - (*oldCoord)[dim_I] ) /
					movementSpeedDivisor;
				movementVector[dim_I] *= movementSign;	
				if ( movementSign == -1 ) {
					movementVector[dim_I] *= (float)movementSpeedDivisor / (movementSpeedDivisor-1); 
				}
				newParticleCoord[dim_I] = (*oldCoord)[dim_I] + movementVector[dim_I];
			}
			memcpy( currParticle->velocity, movementVector, 3*sizeof(double) ); 

			Journal_Printf( stream, "\t\tChanging its coords from (%f,%f,%f) to (%f,%f,%f):\n",
				(*oldCoord)[0], (*oldCoord)[1], (*oldCoord)[2],
				newParticleCoord[0], newParticleCoord[1], newParticleCoord[2] );

			for ( dim_I=0; dim_I < 3; dim_I++ ) {
				currParticle->coord[dim_I] = newParticleCoord[dim_I];
			}
		}
	}

	Swarm_UpdateAllParticleOwners( swarm );
}
unsigned GeneralSwarm_IntegrationPointMap( void* _self, void* _intSwarm, unsigned elementId, unsigned intPtCellId ){
    GeneralSwarm* self  = (GeneralSwarm*)_self;
    IntegrationPointsSwarm*	intSwarm = (IntegrationPointsSwarm*)_intSwarm;
    Mesh*	                intMesh  = (Mesh*)intSwarm->mesh;
    SwarmMap* map = NULL;

    // first, lets check if the int swarm is mirroring a general swarm
    if (intSwarm->mirroredSwarm == (Swarm*)self)
    {
        // ok, it is a mirrored swarm
        return Swarm_ParticleCellIDtoLocalID(
                                        self,
                                        CellLayout_MapElementIdToCellId( self->cellLayout, elementId ),
                                        intPtCellId );
    } else if ( self->previousIntSwarmMap && self->previousIntSwarmMap->swarm==intSwarm ) { /* next check if previous swarmmap */
        map = self->previousIntSwarmMap;
    } else {
        /* ok, previous is not our guy, check other existing: */
        int ii;
        for (ii=0; ii<List_GetSize(self->intSwarmMapList); ii++) {
            map = *(SwarmMap**)List_GetItem(self->intSwarmMapList, ii);
            if ( map->swarm==intSwarm ){
                self->previousIntSwarmMap = map;
                break;
            }
        }
        // if we've gotten to this point, there is no corresponding map.. let's create one */
        map = SwarmMap_New( intSwarm );
        // add to list
        List_Append( self->intSwarmMapList, (void*)&map );
        self->previousIntSwarmMap = map;
        // also add to int swarm incase it moves
        List_Append( intSwarm->swarmsMappedTo, (void*)&map );

    }
    
    unsigned matPointLocalIndex;
    if ( SwarmMap_Map(map,elementId,intPtCellId,&matPointLocalIndex) ) {
        /* ok, map found, return value */
        return matPointLocalIndex;
    } else {
        /* not found... damn.. lets go ahead and find nearest neighbour */
        
        /* lets check some things */
        Journal_Firewall(
            Stg_Class_IsInstance( self->cellLayout, ElementCellLayout_Type ),
            NULL,
            "Error In func %s: %s expects a materialSwarm with cellLayout of type ElementCellLayout.",
            __func__, self->type
        );

        Journal_Firewall(
            intSwarm->mesh==(FeMesh*)((ElementCellLayout*)self->cellLayout)->mesh,
            Journal_Register( Error_Type, (Name)self->type  ),
            "Error - in %s(): Mapper requires both the MaterialSwarm and\n"
            "the IntegrationSwarm to live on the same mesh.\n"
            "Here the MaterialSwarm %s lives in the mesh %s\n"
            "and the IntegrationSwarm %s lives in the mesh %s.",
            self->name, ((ElementCellLayout*)self->cellLayout)->mesh->name,
            intSwarm->name, intSwarm->mesh->name
        );
        
        Cell_Index cell_I = CellLayout_MapElementIdToCellId( intSwarm->cellLayout, elementId );
        Cell_Index cell_M = CellLayout_MapElementIdToCellId(     self->cellLayout, elementId );

        IntegrationPoint* integrationPoint = (IntegrationPoint*)Swarm_ParticleInCellAt( intSwarm, cell_I, intPtCellId );
        
        /* Convert integration point local to global coordinates */
        Coord global;
        FeMesh_CoordLocalToGlobal( intMesh, elementId, integrationPoint->xi, (double*) &global );

        /* now lets sweep material points to find our closest friend */
        double         distance2_min = DBL_MAX;
        double         distance2;
        Particle_Index particle_M;
        unsigned cellPartCount = self->cellParticleCountTbl[ cell_M ];
        
        Journal_Firewall( cellPartCount,
            Journal_Register( Error_Type, (Name)self->type  ),
            "Error - in %s(): There doesn't appear to be any particles\n"
            "within the current cell (%u).\n",
            self->name, cell_M );

        for ( particle_M = 0; particle_M < cellPartCount; particle_M++ ) {
            GlobalParticle* materialPoint = (GlobalParticle*)Swarm_ParticleInCellAt( self, cell_M, particle_M );
            distance2 = pow( global[0] - materialPoint->coord[0], 2 ) + pow( global[1] - materialPoint->coord[1], 2 );
            if( self->dim == 3 )
                distance2 += pow( global[2] - materialPoint->coord[2], 2 );
            if ( distance2 < distance2_min ){
                distance2_min = distance2;
                matPointLocalIndex = Swarm_ParticleCellIDtoLocalID( self, cell_M, particle_M );
            }
        }
        
        /* ok, we've found our nearest friend. record to mapping */
        SwarmMap_Insert(map,elementId,intPtCellId,matPointLocalIndex);

    }
    
    return matPointLocalIndex;
}
void _GeneralSwarm_Initialise( void* swarm, void* data )
{
   GeneralSwarm*	self 	= (GeneralSwarm*) swarm;
   AbstractContext* 	context = (AbstractContext*)self->context;
   Index            	var_I	= 0;

   _Swarm_Initialise( self, data );

   if( self->escapedRoutine != NULL) Stg_Component_Initialise( self->escapedRoutine, data , False );

   for( var_I = 0 ; var_I < self->nSwarmVars ; var_I++ )
   {
      Stg_Component_Initialise( self->swarmVars[var_I], data , False );
   }


   /** if loading from checkpoint, particle materials etc have already been loaded in Swarm_Build() - */
   /** possibly need to check for empty cells (and populate) if performing a interpolation restart */
   if ( context && True == context->loadFromCheckPoint )
   {
      if ( (True == self->isSwarmTypeToCheckPointAndReload) && (True == context->interpolateRestart) )
      {
         Particle_InCellIndex cParticle_I         = 0;
         Particle_InCellIndex particle_I          = 0;
         GlobalParticle*      particle            = NULL;
         double               minDistance         = HUGE_VAL;
         double               distanceToParticle;
         Dimension_Index      dim = self->dim;
         Cell_DomainIndex     dCell_I;
         Cell_LocalIndex      lCell_I;
         Cell_DomainIndex     belongsToCell_I = 0;
         GlobalParticle*       matNewParticle;
         GlobalParticle*       matParticleToSplit;
         Particle_Index       matNewParticle_IndexOnCPU;
         Coord                xi;
         Coord                coord;
         unsigned             nEmptyCells = 0;
         Index*				   cellID = NULL;
         Index*				   particleCPUID = NULL;
         unsigned             count;
         unsigned             ii;

         /** first determine how many local cells are empty */
         for( lCell_I = 0 ; lCell_I < self->cellLocalCount ; lCell_I++ )
            if (self->cellParticleCountTbl[lCell_I] == 0) nEmptyCells++;

         /** create arrays which will be later used to populate cells */
         cellID        = Memory_Alloc_Array( Index, nEmptyCells, "Cell ID for cell to be populated" );
         particleCPUID = Memory_Alloc_Array( Index, nEmptyCells, "particle ID for particle to populate cell" );

         count = 0;
         for( lCell_I = 0 ; lCell_I < self->cellLocalCount ; lCell_I++ )
         {
            minDistance = HUGE_VAL;
            if (self->cellParticleCountTbl[lCell_I] == 0)
            {
               /** select the centre of the current cell */
               xi[0] = 0;
               xi[1] = 0;
               xi[2] = 0;

               Journal_Firewall(
                                Stg_Class_IsInstance( self->cellLayout, ElementCellLayout_Type ),
                                NULL,
                                "Error In func %s: When performing interpolation restart, cellLayout must be of type ElementCellLayout.",
                                __func__ );

               /** get global coord */
               FeMesh_CoordLocalToGlobal( ((ElementCellLayout*)self->cellLayout)->mesh, lCell_I, xi, coord );

               for( dCell_I = 0 ; dCell_I < self->cellDomainCount ; dCell_I++ )
               {
                  /** Loop over particles find closest to cell centre */
                  for( cParticle_I = 0 ; cParticle_I < self->cellParticleCountTbl[dCell_I] ; cParticle_I++ )
                  {
                     particle = (GlobalParticle*)Swarm_ParticleInCellAt( self, dCell_I, cParticle_I );

                     /** Calculate distance to particle */
                     distanceToParticle =
                        (particle->coord[ I_AXIS ] - coord[ I_AXIS ]) *
                        (particle->coord[ I_AXIS ] - coord[ I_AXIS ]) +
                        (particle->coord[ J_AXIS ] - coord[ J_AXIS ]) *
                        (particle->coord[ J_AXIS ] - coord[ J_AXIS ]) ;

                     if (dim == 3)
                     {
                        distanceToParticle +=
                           (particle->coord[ K_AXIS ] - coord[ K_AXIS ]) *
                           (particle->coord[ K_AXIS ] - coord[ K_AXIS ]) ;
                     }
                     /** Don't do square root here because it is unnessesary: i.e. a < b <=> sqrt(a) < sqrt(b) */
                     /** Check if this is the closest particle */
                     if (minDistance > distanceToParticle)
                     {
                        particle_I       = cParticle_I;
                        minDistance      = distanceToParticle;
                        belongsToCell_I  = dCell_I;
                     }
                  }
               }

               /** create new particle which will be placed at centre of empty cell */
               matNewParticle      = (GlobalParticle*) Swarm_CreateNewParticle( self, &matNewParticle_IndexOnCPU );
               /** grab closest particle, which we will copy */
               matParticleToSplit  = (GlobalParticle*) Swarm_ParticleInCellAt( self, belongsToCell_I, particle_I );
               /** copy - even though self->particleExtensionMgr->finalSize maybe biger than sizeof(GlobalParticle)
                   the addressing of the copy is the important part
                */
               memcpy( matNewParticle, matParticleToSplit, self->particleExtensionMgr->finalSize );
               /** set the owningCell to the cellDomainCount so that its owningCell will be reinitialised (not sure if necessary) */
               matNewParticle->owningCell = self->cellDomainCount;
               /** Copy new global position (cell centre) to coord on new mat particle */
               memcpy( matNewParticle->coord, coord, sizeof(Coord) );

               /** we now store the required information to populate empty cells */
               /** note that cells are not populated at this point, as this may interfere
                   with the 0th order interpolation we are performing */
               cellID[count]        = lCell_I;
               particleCPUID[count] = matNewParticle_IndexOnCPU;
               count++;

            }
         }
         /** populate empty cells */
         for(ii = 0 ; ii < count ; ii++)
            Swarm_AddParticleToCell( self, cellID[ii], particleCPUID[ii] );
         Memory_Free( cellID );
         Memory_Free( particleCPUID );
      }
      /* TODO: print info / debug message */
   }

}
void _MatrixAssemblyTerm_NA__NB__Fn_AssembleElement(
   void*                                              matrixTerm,
   StiffnessMatrix*                                   stiffnessMatrix,
   Element_LocalIndex                                 lElement_I,
   SystemLinearEquations*                             sle,
   FiniteElementContext*                              context,
   double**                                           elStiffMat )
{
   MatrixAssemblyTerm_NA__NB__Fn* self = (MatrixAssemblyTerm_NA__NB__Fn*)matrixTerm;
   Swarm*                              swarm        = self->integrationSwarm;
   FeVariable*                         variable_row = stiffnessMatrix->rowVariable;
   FeVariable*                         variable_col = stiffnessMatrix->columnVariable;
   Dimension_Index                     dim          = stiffnessMatrix->dim;
   Particle_InCellIndex                cParticle_I, cellParticleCount;
   Node_ElementLocalIndex              nodesPerEl_row, nodesPerEl_col;

   Dof_Index                           dofPerNode_col;
   Index                               row, col; /* Indices into the stiffness matrix */
   Node_ElementLocalIndex              rowNode_I, colNode_I;
   Dof_Index                           colDof_I;
   double                              *xi, *Ni, *Mi;
   double                              detJac, weight, F, factor;
   IntegrationPoint*                   intPoint;
   Cell_Index                          cell_I;
   ElementType*                        elementType_row;
   ElementType*                        elementType_col;


   MatrixAssemblyTerm_NA__NB__Fn_cppdata* cppdata = (MatrixAssemblyTerm_NA__NB__Fn_cppdata*)self->cppdata;
   debug_dynamic_cast<ParticleInCellCoordinate*>(cppdata->input->localCoord())->index() = lElement_I;  // set the elementId as the owning cell for the particleCoord
   cppdata->input->index() = lElement_I;

   FeMesh*                 geometryMesh = ( self->geometryMesh ? self->geometryMesh : variable_row->feMesh );
   ElementType*            geometryElementType;

   /* Set the element type */
   geometryElementType = FeMesh_GetElementType( geometryMesh, lElement_I );

   elementType_row = FeMesh_GetElementType( variable_row->feMesh, lElement_I );
   nodesPerEl_row = elementType_row->nodeCount;
   elementType_col = FeMesh_GetElementType( variable_col->feMesh, lElement_I );
   nodesPerEl_col = elementType_col->nodeCount;

   dofPerNode_col = variable_col->fieldComponentCount;

   // allocate shape function array, Mi and Ni
   if( nodesPerEl_row > self->max_nElNodes_row ) {
      /* reallocate */
      if (self->Mi) free(self->Mi);
      self->Mi = (double*)AllocArray( double, nodesPerEl_row );
      self->max_nElNodes_row = nodesPerEl_row;
   }

   if( nodesPerEl_col > self->max_nElNodes_col ) {
      if( self->Ni) free(self->Ni);
      self->Ni = (double*)AllocArray(double, nodesPerEl_col );
      self->max_nElNodes_col = nodesPerEl_col;
   }
   Ni = self->Ni;
   if (elementType_row == elementType_col)
      Mi = Ni;

   cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
   cellParticleCount = swarm->cellParticleCountTbl[ cell_I ];

   for ( cParticle_I = 0 ; cParticle_I < cellParticleCount ; cParticle_I++ ) {
      debug_dynamic_cast<ParticleInCellCoordinate*>(cppdata->input->localCoord())->particle_cellId(cParticle_I);  // set the particleCoord cellId
      /* get integration point information */
      intPoint = (IntegrationPoint*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
      xi = intPoint->xi;
      weight = intPoint->weight;

      /* evaluate shape function and jacobian determinant */
      detJac = ElementType_JacobianDeterminant( geometryElementType, geometryMesh, lElement_I, xi, dim );
      ElementType_EvaluateShapeFunctionsAt( elementType_col, xi, Ni );
      if (elementType_row != elementType_col)
         ElementType_EvaluateShapeFunctionsAt( elementType_row, xi, Mi );

      // /* evaluate function */
      const IO_double* funcout = debug_dynamic_cast<const IO_double*>(cppdata->func(cppdata->input.get()));
      F = funcout->at();

      factor = weight*detJac*F;
      /* build stiffness matrix */
      for ( rowNode_I = 0; rowNode_I < nodesPerEl_row ; rowNode_I++) {
         for (colNode_I = 0; colNode_I < nodesPerEl_col; colNode_I++ ) {
            for( colDof_I=0; colDof_I<dofPerNode_col; colDof_I++) {
                  /* note that we use the row dof count here too */
                  row = rowNode_I*dofPerNode_col + colDof_I;
                  col = colNode_I*dofPerNode_col + colDof_I;
                  elStiffMat[row][col] += factor * ( Ni[rowNode_I] * Mi[colNode_I] );
            }
         }
      }
   }
}
void _MatrixAssemblyTerm_NA_i__NB_i__Fn_AssembleElement(
    void*                                              matrixTerm,
    StiffnessMatrix*                                   stiffnessMatrix,
    Element_LocalIndex                                 lElement_I,
    SystemLinearEquations*                             sle,
    FiniteElementContext*                              context,
    double**                                           elStiffMat )
{
    MatrixAssemblyTerm_NA_i__NB_i__Fn*   self = (MatrixAssemblyTerm_NA_i__NB_i__Fn*)matrixTerm;
    Swarm*                              swarm        = self->integrationSwarm;
    FeVariable*                         variable1    = stiffnessMatrix->rowVariable;
    Dimension_Index                     dim          = stiffnessMatrix->dim;
    IntegrationPoint*                   currIntegrationPoint;
    double*                             xi;
    double                              weight;
    Particle_InCellIndex                cParticle_I, cellParticleCount;
    Index                               nodesPerEl;
    Index                               A,B;
    Index                               i;
    double**                            GNx;
    double                              detJac;
    double                              F;
    Cell_Index                          cell_I;
    ElementType*                        elementType;

    /* Set the element type */
    elementType = FeMesh_GetElementType( variable1->feMesh, lElement_I );
    nodesPerEl = elementType->nodeCount;

    if( nodesPerEl > self->max_nElNodes ) {
        /* reallocate */
        if (self->GNx)
            free(self->GNx);
        self->GNx = (double**)AllocArray2D( double, dim, nodesPerEl );
        self->max_nElNodes = nodesPerEl;
    }
    GNx = self->GNx;

    MatrixAssemblyTerm_NA_i__NB_i__Fn_cppdata* cppdata = (MatrixAssemblyTerm_NA_i__NB_i__Fn_cppdata*)self->cppdata;

    debug_dynamic_cast<ParticleInCellCoordinate>(cppdata->input->localCoord())->index() = lElement_I;  // set the elementId as the owning cell for the particleCoord
    cppdata->input->index() = lElement_I;  // set the elementId for the fem coordinate

    cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
    cellParticleCount = swarm->cellParticleCountTbl[ cell_I ];

    for( cParticle_I = 0 ; cParticle_I < cellParticleCount ; cParticle_I++ ) {
        debug_dynamic_cast<ParticleInCellCoordinate>(cppdata->input->localCoord())->particle_cellId(cParticle_I);  // set the particleCoord cellId
        currIntegrationPoint = (IntegrationPoint*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );

        xi = currIntegrationPoint->xi;
        weight = currIntegrationPoint->weight;

        ElementType_ShapeFunctionsGlobalDerivs(
            elementType,
            variable1->feMesh, lElement_I,
            xi, dim, &detJac, GNx );

        /* evaluate function */
        std::shared_ptr<const IO_double> funcout = debug_dynamic_cast<const IO_double>(cppdata->func(cppdata->input));
        F = funcout->at();

        for( A=0; A<nodesPerEl; A++ )
            for( B=0; B<nodesPerEl; B++ )
                for ( i = 0; i < dim ; i++ )
                    elStiffMat[A][B] += detJac * weight * GNx[i][A] * GNx[i][B] * F;
    }
}
void _MatAssembly_NA__Fi__NB_AssembleElement(
      void*                                              matrixTerm,
      StiffnessMatrix*                                   stiffnessMatrix,
      Element_LocalIndex                                 lElement_I,
      SystemLinearEquations*                             sle,
      FiniteElementContext*                              context,
      double**                                           elStiffMat )
{
   MatAssembly_NA__Fi__NB*   self = (MatAssembly_NA__Fi__NB*)matrixTerm;
   Swarm*                              swarm        = self->integrationSwarm;
   FeVariable*                         rowFeVar     = stiffnessMatrix->rowVariable;
   FeVariable*                         colFeVar     = stiffnessMatrix->columnVariable;
   Dimension_Index                     dim          = stiffnessMatrix->dim;
   int                                 rowDofs = rowFeVar->fieldComponentCount; // number of dofs per row node
   int                                 colDofs = colFeVar->fieldComponentCount; // number of dofs per row node
   IntegrationPoint*                   currIntegrationPoint;
   double*                             xi;
   double                              weight;
   Particle_InCellIndex                cParticle_I, cellParticleCount;
   Index                               rowNodes; // number of row nodes per element
   Index                               colNodes; // number of col nodes per element
   Index                               A,B;
   Index                               i;
   double                              detJac;
   double                              gradRho_rtp[3], gradRho_xyz[3], rho, xyz[3];
   Cell_Index                          cell_I;
   ElementType*                        rowElementType, *colElementType;
   double                              N[27], M[6];

   /* Set the element type */
   rowElementType = FeMesh_GetElementType( rowFeVar->feMesh, lElement_I ); rowNodes = rowElementType->nodeCount;
   colElementType = FeMesh_GetElementType( colFeVar->feMesh, lElement_I ); colNodes = colElementType->nodeCount;

   cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
   cellParticleCount = swarm->cellParticleCountTbl[ cell_I ];

   for( cParticle_I = 0 ; cParticle_I < cellParticleCount ; cParticle_I++ ) {

      currIntegrationPoint = (IntegrationPoint*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );

      xi = currIntegrationPoint->xi;
      weight = currIntegrationPoint->weight;

      /* Calculate Determinant of Jacobian and Shape Functions */
      detJac = ElementType_JacobianDeterminant( colElementType, colFeVar->feMesh, lElement_I, xi, dim );
      ElementType_EvaluateShapeFunctionsAt( rowElementType, xi, M );
      ElementType_EvaluateShapeFunctionsAt( colElementType, xi, N ); 

      /* evaluate ppc function */
      PpcManager_Get( self->ppcManager, lElement_I, currIntegrationPoint, self->rho, &rho );

      // ASSUMES: gradRho_rtp[0] is the radial component of grad(rho)
      PpcManager_Get( self->ppcManager, lElement_I, currIntegrationPoint, self->grad_rho, &(gradRho_rtp[0]) );
      gradRho_rtp[0]=-1*gradRho_rtp[0]/rho; gradRho_rtp[1]=0; gradRho_rtp[2]=0; // minus one because depth is the negative of radial-axis
      // This needs to be converted to an xyz basis vector
      FeMesh_CoordLocalToGlobal( colFeVar->feMesh, lElement_I, xi, xyz );
      Spherical_VectorRTP2XYZ( gradRho_rtp, xyz, 2, gradRho_xyz );

      for( A=0; A<rowNodes; A++ ) {
         for( B=0; B<colNodes; B++ ) {
            for ( i = 0; i < colDofs ; i++ ) {
               elStiffMat[rowDofs*A][colDofs*B+i] += detJac * weight * M[A] * gradRho_xyz[i] * N[B] ;
            }
         }
      }
   }
}
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] );
}
void PCDVCSuite_TestCircleInterface( PCDVCSuiteData* data ) {
   Swarm*             integrationSwarm = (Swarm*)LiveComponentRegister_Get( data->context->CF->LCRegister, (Name)"integrationSwarm" );
   Swarm*             materialSwarm    = (Swarm*)LiveComponentRegister_Get( data->context->CF->LCRegister, (Name)"materialPoints" );
   FeMesh*            mesh             = (FeMesh*)LiveComponentRegister_Get( data->context->CF->LCRegister, (Name)"linearMesh" );
   //WeightsCalculator* weights          = (WeightsCalculator*)LiveComponentRegister_Get( data->context->CF->LCRegister, (Name)"weights" );
   FeVariable*        feVariable;
   Element_LocalIndex lElement_I       = 0;
   double             analyticValue    = 0.0;
   double             integral         = 0.0;
   double             error;
   double             errorSquaredSum  = 0.0;
   double             errorSum         = 0.0;
   Index              loop_I;
   Index              count            = Dictionary_GetUnsignedInt_WithDefault( data->context->dictionary, "SampleSize", 5000 );
   void*              generic          = NULL;
   IntegrationPoint*  intParticle;
   MaterialPoint*     materialPoint;
   double             mean;
   double             standardDeviation;

   /* Create FeVariable */
   feVariable = FeVariable_New_Full(
      "feVariable",
      (DomainContext*)data->context,
      mesh,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL, 
      1,
      data->context->dim,
      False,
      False,
      False,
      MPI_COMM_WORLD,
      data->context->fieldVariable_Register );

   feVariable->_interpolateWithinElement = CircleInterface;
   analyticValue = M_PI;

   for ( loop_I = 0 ; loop_I < count ; loop_I++ ) {
      /* Layout Particles */
      Swarm_Random_Seed( (long)loop_I );
      _Swarm_InitialiseParticles( materialSwarm, generic );

      _IntegrationPointsSwarm_UpdateHook( NULL, integrationSwarm );

      /* The following function should not be called here as it is already called ultimately via the above function */
      /* calling this function again causes a first iteration in Lloyd's algorithm for the Voronoi cells which we don't want here */
      //WeightsCalculator_CalculateCell( weights, integrationSwarm, lElement_I );
      if( loop_I % 10 == 0 ) {
         int i;

         for( i = 0 ; i < integrationSwarm->cellParticleCountTbl[0] ; i++ ) {
            intParticle = (IntegrationPoint*)Swarm_ParticleInCellAt( integrationSwarm, 0, i );
            materialPoint = (MaterialPoint*)Swarm_ParticleInCellAt( materialSwarm, 0, i );
            /* printf("In %s M(%10.7lf %10.7lf)I(%10.7lf %10.7lf)W(%.4lf)particle layout type %s: point %d in cell %d\n",__func__,
               materialPoint->coord[0], materialPoint->coord[1], intParticle->xi[0], intParticle->xi[1], intParticle->weight, 
               materialSwarm->particleLayout->type, i, 0);
            printf("%lf %lf\n",intParticle->xi[0],intParticle->xi[1]); */
         }
      }

      /* Evaluate Integral */
      integral = FeVariable_IntegrateElement( feVariable, integrationSwarm, lElement_I );

      /* Calculate Error */
      error = fabs( ( integral - analyticValue )/( analyticValue ) );
      errorSum += error;
      errorSquaredSum += error*error;
   }

   /* Calculate Mean and Standard Deviation */
   mean = errorSum / (double)count;
   standardDeviation = sqrt( errorSquaredSum / (double)count - mean * mean );
   printf("In %s: Mean %lf SD %lf Sol %lf Count = %d\n", __func__, mean, standardDeviation, analyticValue,   count);
   Stg_Component_Destroy( feVariable, NULL, True );

   compareAgainstReferenceSolution( data->context, data->stream, mean, standardDeviation, "testPCDVC_CircleInterface.expected" );
}
/* remember this only has to initialise one cell of particles at a time */
void _TriGaussParticleLayout_InitialiseParticlesOfCell( void* triGaussParticleLayout, void* _swarm, Cell_Index cell_I )
{
	#define MAX_DIM 3
	#define MAX_GAUSS_POINTS_2D 1
	#define MAX_GAUSS_POINTS_3D 1
	TriGaussParticleLayout*   self = (TriGaussParticleLayout*)triGaussParticleLayout;
	Swarm*                    swarm = (Swarm*)_swarm;
	IntegrationPoint*         integrationPoint = NULL;

	Particle_InCellIndex      cParticle_I = 0;

	Particle_InCellIndex ppc;
	int dim;
	double weight[1];
	double xi[20][3];
	static int beenHere = 0;
	int d;
	
	dim = self->dim;
	ppc = self->particlesPerCell;
	
	if( dim == 2 ) {
		if( ppc == 1 ) {
			weight[0] = 0.5;

			xi[0][0] = 0.333333333333;
			xi[0][1] = 0.333333333333;				
		}
		if( ppc > MAX_GAUSS_POINTS_2D ) {
			Journal_Firewall(
				ppc > MAX_GAUSS_POINTS_2D,
				Journal_MyStream( Error_Type, self ),
				"In %s(), error: particlesPerCell greater than implementated tabulated gauss values of %d\n",
				__func__,
				MAX_GAUSS_POINTS_2D );
		}
	}
	else if ( dim == 3 ) {
		if( ppc == 1 ) {
			weight[0] = 0.5;			

			xi[0][0] = 0.333333333333;
			xi[0][1] = 0.333333333333;				
			xi[0][2] = 0.333333333333;				
		}
		if( ppc > MAX_GAUSS_POINTS_3D ) {
			Journal_Firewall(
				ppc > MAX_GAUSS_POINTS_3D,
				Journal_MyStream( Error_Type, self ),
				"In %s(), error: particlesPerCell greater than implementated tabulated gauss values of %d\n",
				__func__,
				MAX_GAUSS_POINTS_3D );
		}
	}

	assert( ppc <= swarm->cellParticleCountTbl[cell_I] );
	
	for ( cParticle_I = 0; cParticle_I < ppc; cParticle_I++ ) {
		integrationPoint = (IntegrationPoint*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
		integrationPoint->owningCell = cell_I;
			
		for( d = 0; d < dim; d++ ) {
			/*integrationPoint->coord[d] = xi[cParticle_I][d];*/
			integrationPoint->xi[d] = xi[cParticle_I][d];
		}
		
		integrationPoint->weight = weight[cParticle_I];
		
		beenHere = 1;
	}	
	
	
}
void _SUPGAdvDiffTermPpc_AssembleElement(
   void*              forceTerm,
   ForceVector*       forceVector,
   Element_LocalIndex lElement_I,
   double*            elementResidual )
{
   SUPGAdvDiffTermPpc*    self = Stg_CheckType( forceTerm, SUPGAdvDiffTermPpc );
   AdvectionDiffusionSLE* sle = self->sle;
   Swarm*                 swarm = self->integrationSwarm;
   Particle_Index         lParticle_I;
   Particle_Index         cParticle_I;
   Particle_Index         cellParticleCount;
   Cell_Index             cell_I;    
   IntegrationPoint*      particle;
   FeVariable*            phiField = self->phiField;
   Dimension_Index        dim = forceVector->dim;
   double                 velocity[3];
   double                 phi, phiDot;
   double                 detJac;
   double*                xi;
   double                 totalDerivative, diffusionTerm;
   double                 diffusivity = 0;
   ElementType*           elementType = FeMesh_GetElementType( phiField->feMesh, lElement_I );
   Node_Index             elementNodeCount = elementType->nodeCount;
   Node_Index             node_I;
   double                 factor;
   double**               GNx;
   double*                phiGrad;
   double*                Ni;
   double*                SUPGNi;
   double                 supgfactor;
   double                 udotu, perturbation;
   double                 upwindDiffusivity;
   int                    err;

   GNx = self->GNx;
   phiGrad = self->phiGrad;
   Ni = self->Ni;
   SUPGNi = self->SUPGNi;
   
   upwindDiffusivity = SUPGAdvDiffTermPpc_UpwindDiffusivity( self, sle, swarm, phiField->feMesh, lElement_I, dim );

   /* Determine number of particles in element */
   cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
   cellParticleCount = swarm->cellParticleCountTbl[ cell_I ];
   
   for ( cParticle_I = 0 ; cParticle_I < cellParticleCount ; cParticle_I++ ) {
      particle = (IntegrationPoint*)Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
      xi          = particle->xi;
      
      /* Evaluate Shape Functions */
      ElementType_EvaluateShapeFunctionsAt(elementType, xi, Ni);

      /* Calculate Global Shape Function Derivatives */
      ElementType_ShapeFunctionsGlobalDerivs( 
         elementType,
         phiField->feMesh, lElement_I,
         xi, dim, &detJac, GNx );
      
      /* Calculate Velocity */
      FeVariable_InterpolateFromMeshLocalCoord( self->velocityField, phiField->feMesh, lElement_I, xi, velocity );

      /* Build the SUPG shape functions */
      udotu = velocity[I_AXIS]*velocity[I_AXIS] + velocity[J_AXIS]*velocity[J_AXIS];
      if(dim == 3) udotu += velocity[ K_AXIS ] * velocity[ K_AXIS ];

      supgfactor = upwindDiffusivity / udotu;
      for ( node_I = 0 ; node_I < elementNodeCount ; node_I++ ) {
         /* In the case of per diffusion - just build regular shape functions */
         if ( fabs(upwindDiffusivity) < SUPG_MIN_DIFFUSIVITY ) {
            SUPGNi[node_I] = Ni[node_I];
            continue;
         }
         
         perturbation = velocity[ I_AXIS ] * GNx[ I_AXIS ][ node_I ] + velocity[ J_AXIS ] * GNx[ J_AXIS ][ node_I ];
         if (dim == 3)
               perturbation = perturbation + velocity[ K_AXIS ] * GNx[ K_AXIS ][ node_I ];
         
         /* p = \frac{\bar \kappa \hat u_j w_j }{ ||u|| } -  Eq. 3.2.25 */
         perturbation = supgfactor * perturbation;
         
         SUPGNi[node_I] = Ni[node_I] + perturbation;
      }  
      
      /* Calculate phi on particle */
      _FeVariable_InterpolateNodeValuesToElLocalCoord( phiField, lElement_I, xi, &phi );

      /* Calculate Gradients of Phi */
      FeVariable_InterpolateDerivatives_WithGNx( phiField, lElement_I, GNx, phiGrad );

      /* Calculate time derivative of phi */
      _FeVariable_InterpolateNodeValuesToElLocalCoord( sle->phiDotField, lElement_I, xi, &phiDot );
      
      /* Calculate total derivative (i.e. Dphi/Dt = \dot \phi + u . \grad \phi) */
      totalDerivative = phiDot + StGermain_VectorDotProduct( velocity, phiGrad, dim );

      /* get the diffusivity */
      err = PpcManager_Get( self->mgr, lElement_I, particle, self->diffusivityLabel, &diffusivity );
      if( err ) assert(0);

      /* Add to element residual */
      factor = particle->weight * detJac;
      for( node_I = 0 ; node_I < elementNodeCount ; node_I++ ) {
         /* Calculate Diffusion Term */
         diffusionTerm = diffusivity * ( GNx[0][node_I] * phiGrad[0] + GNx[1][node_I] * phiGrad[1] );
         if(dim == 3)
            diffusionTerm += diffusivity * GNx[2][ node_I ] * phiGrad[2] ;
         
         elementResidual[ node_I ] -=  factor * ( SUPGNi[ node_I ] * totalDerivative + diffusionTerm );
      }
   }
}
void _ThermalBuoyancyForceTerm_AssembleElement( void* forceTerm, ForceVector* forceVector, Element_LocalIndex lElement_I, double* elForceVec ) {
	ThermalBuoyancyForceTerm*	self = Stg_CheckType( forceTerm, ThermalBuoyancyForceTerm );
	Swarm*							swarm = self->integrationSwarm;
	Dimension_Index				dim = forceVector->dim;
	IntegrationPoint*				particle;
	FeVariable*						temperatureField;
	FeMesh*							mesh;
	FeMesh*							temperatureMesh;
	double*							xi;
	Particle_InCellIndex			cParticle_I;
	Particle_InCellIndex			cellParticleCount;
	Element_NodeIndex				elementNodeCount;
	Node_ElementLocalIndex		node_I;
	ElementType*					elementType;
	Dof_Index						dofsPerNode;
	Cell_Index						cell_I;
	double							detJac;
	double							factor;
	/*double							Ni[8];*/
	double							Ni[27];
	double							force;
	double							rayleighNumber;
	double							temperature;

	/* Get context extension */
	rayleighNumber   = self->rayleighNumber;
	temperatureField = self->temperatureField;
	temperatureMesh  = temperatureField->feMesh;

	/* Since we are integrating over the velocity mesh - we want the velocity mesh here and not the temperature mesh */
	mesh             = forceVector->feVariable->feMesh;
	
	/* Set the element type */
	elementType      = FeMesh_GetElementType( mesh, lElement_I ); 
	elementNodeCount = elementType->nodeCount;

	/* assumes constant number of dofs per element */
	dofsPerNode = dim;
	
	cell_I = CellLayout_MapElementIdToCellId( swarm->cellLayout, lElement_I );
	cellParticleCount = swarm->cellParticleCountTbl[ cell_I ];
	
	for ( cParticle_I = 0 ; cParticle_I < cellParticleCount ; cParticle_I++ ) {
		particle = (IntegrationPoint*) Swarm_ParticleInCellAt( swarm, cell_I, cParticle_I );
		xi       = particle->xi;
		
		/* Calculate Determinant of Jacobian and Shape Functions */
		detJac = ElementType_JacobianDeterminant( elementType, mesh, lElement_I, xi, dim );
		ElementType_EvaluateShapeFunctionsAt( elementType, xi, Ni );

		/* Field Get Temperature from Field Variable */
		FeVariable_InterpolateFromMeshLocalCoord( temperatureField, mesh, lElement_I, xi, &temperature );

		force = rayleighNumber * temperature;

		factor = detJac * particle->weight * force;
		for( node_I = 0 ; node_I < elementNodeCount ; node_I++ ) 
			elForceVec[node_I * dofsPerNode + J_AXIS ] += factor * Ni[ node_I ] ;
		
	}
}