/* Integrate Every Step and dump to file */ void Underworld_Vrms_Dump( void* _context ) { UnderworldContext* context = (UnderworldContext* ) _context; Mesh* mesh; double maxCrd[3], minCrd[3]; double integral; double vrms; double volume = 0.0; Dimension_Index dim = context->dim; Underworld_Vrms* self; self = (Underworld_Vrms*)LiveComponentRegister_Get( context->CF->LCRegister, (Name)Underworld_Vrms_Type ); mesh = (Mesh*)self->velocitySquaredField->feMesh; Mesh_GetGlobalCoordRange( mesh, minCrd, maxCrd ); /* Sum integral */ integral = FeVariable_Integrate( self->velocitySquaredField, self->gaussSwarm ); /* Get Volume of Mesh - TODO Make general for irregular meshes */ volume = ( maxCrd[ I_AXIS ] - minCrd[ I_AXIS ] ) * ( maxCrd[ J_AXIS ] - minCrd[ J_AXIS ] ); if ( dim == 3 ) volume *= maxCrd[ K_AXIS ] - minCrd[ K_AXIS ]; /* Calculate Vrms * V_{rms} = \sqrt{ \frac{ \int_\Omega \mathbf{u . u} d\Omega }{\Omega} } */ vrms = sqrt( integral / volume ); /* Print data to file */ StgFEM_FrequentOutput_PrintValue( context, vrms ); /* Put Value onto context */ self->vrms = vrms; }
void _Trubitsyn2006_PressureFunction( void* _context, double* coord, double* pressure ) { UnderworldContext* context = (UnderworldContext*)_context; Trubitsyn2006* self = (Trubitsyn2006*)LiveComponentRegister_Get( context->CF->LCRegister, (Name)Trubitsyn2006_Type ); double T0 = self->T0; double Ra = self->Ra; double x; double y; XYZ min, max; double eta; double eta01; double upperLeftCorner[] = { 0.0, 1.0, 0.0 }; Mesh_GetGlobalCoordRange( self->velocityField->feMesh, min, max ); x = coord[ I_AXIS ] - min[ I_AXIS ]; y = coord[ J_AXIS ] - min[ J_AXIS ]; /* Get Viscosities */ self->viscosityFunc->apply( self, coord, &eta ); self->viscosityFunc->apply( self, upperLeftCorner, &eta01 ); *pressure = - Ra * ( ( y * y * 0.5 - y + 0.5 ) + 0.5 * T0 / M_PI * ( eta * cos( M_PI * y ) * cos( M_PI * x ) + eta01 ) );/* Equation 46 */ /*printf("pressure from t0 = %g\n", *pressure ); *pressure = - 2.0 * M_PI * v0 * ( eta * cos( M_PI * y ) * cos( M_PI * x ) + eta01 ) - Ra * ( y*y * 0.5 - y + 0.5 ); printf("pressure from v0 = %g\n\n", *pressure );*/ }
void _Trubitsyn2006_StreamFunction( void* _context, double* coord, double* psi ) { UnderworldContext* context = (UnderworldContext*)_context; Trubitsyn2006* self = (Trubitsyn2006*)LiveComponentRegister_Get( context->CF->LCRegister, (Name)Trubitsyn2006_Type ); double v0 = Trubitsyn2006_V0( self ); double x; double y; XYZ min, max; Mesh_GetGlobalCoordRange( self->velocityField->feMesh, min, max ); x = coord[ I_AXIS ] - min[ I_AXIS ]; y = coord[ J_AXIS ] - min[ J_AXIS ]; *psi = - v0 / M_PI * sin( M_PI * y ) * sin( M_PI * x ); /* Equation 40 */ }
void Trubitsyn2006_TemperatureIC( Node_LocalIndex node_lI, Variable_Index var_I, void* _context, void* data, void* _result ) { DomainContext* context = (DomainContext*)_context; FeVariable* temperatureField = NULL; FeMesh* mesh = NULL; double* temperature = (double*) _result; double* coord; double x; double y; Trubitsyn2006* self = NULL; double T0; double Ra; double v0 ; XYZ min, max; double eta; double d_eta_dy; XYZ viscDeriv; self = Stg_ComponentFactory_ConstructByName( context->CF, (Name)Trubitsyn2006_Type, Trubitsyn2006, True, context ); T0 = self->T0; Ra = self->Ra; v0 = Trubitsyn2006_V0( self ); temperatureField = (FeVariable*) FieldVariable_Register_GetByName( context->fieldVariable_Register, "TemperatureField" ); mesh = temperatureField->feMesh; /* Find coordinate of node */ coord = Mesh_GetVertex( mesh, node_lI ); Mesh_GetGlobalCoordRange( mesh, min, max ); x = coord[ I_AXIS ] - min[ I_AXIS ]; y = coord[ J_AXIS ] - min[ J_AXIS ]; self->viscosityFunc->apply( self, coord, &eta ); self->viscosityDerivativeFunc->apply( self, coord, viscDeriv ); d_eta_dy = viscDeriv[ J_AXIS ]; *temperature = 1 - y + T0 * cos( M_PI * x ) * ( eta * sin( M_PI * y ) - d_eta_dy / M_PI * cos( M_PI * y ) ) ; /* Equation 47 */ *temperature = 1 - y + 4.0 * M_PI * v0 / Ra * cos( M_PI * x ) * ( M_PI * eta * sin( M_PI * y ) - d_eta_dy * cos( M_PI * y ) ) ; /* Equation 47 */ }
void SROpGenerator_GenLevelMesh( SROpGenerator* self, unsigned level ) { Stream* errorStream = Journal_Register( ErrorStream_Type, (Name)"SROpGenerator::GenLevelMesh" ); Mesh *fMesh, *cMesh; CartesianGenerator *fGen, *cGen; unsigned nDims; unsigned* cSize; double crdMin[3], crdMax[3]; unsigned d_i; assert( self && Stg_CheckType( self, SROpGenerator ) ); assert( self->meshes ); assert( level < self->nLevels ); fMesh = self->meshes[level + 1]; nDims = Mesh_GetDimSize( fMesh ); fGen = (CartesianGenerator*)fMesh->generator; Journal_Firewall( fGen && !strcmp( fGen->type, CartesianGenerator_Type ), errorStream, "\n" \ "****************************************************************\n" \ "* Error: Simple regular multigrid operator generation requires *\n" \ "* a fine mesh that has been generated with a *\n" \ "* cartesian generator. *\n" \ "****************************************************************\n" \ "\n" ); cGen = CartesianGenerator_New( "", NULL ); CartesianGenerator_SetDimSize( cGen, nDims ); cSize = AllocArray( unsigned, nDims ); for( d_i = 0; d_i < nDims; d_i++ ) cSize[d_i] = fGen->elGrid->sizes[d_i] / 2; CartesianGenerator_SetTopologyParams( cGen, cSize, fGen->maxDecompDims, fGen->minDecomp, fGen->maxDecomp ); Mesh_GetGlobalCoordRange( fMesh, crdMin, crdMax ); CartesianGenerator_SetGeometryParams( cGen, crdMin, crdMax ); CartesianGenerator_SetShadowDepth( cGen, 0 ); FreeArray( cSize ); cMesh = (Mesh*)FeMesh_New( "" ); Mesh_SetGenerator( cMesh, cGen ); FeMesh_SetElementFamily( cMesh, ((FeMesh*)fMesh)->feElFamily ); Stg_Component_Build( cMesh, NULL, False ); Stg_Component_Initialise( cMesh, NULL, False ); self->meshes[level] = cMesh; }
void PeriodicBoundariesManager_AddPeriodicBoundary( void* periodicBCsManager, Axis axis ) { PeriodicBoundariesManager* self = (PeriodicBoundariesManager*)periodicBCsManager; PeriodicBoundary* newPeriodicBoundary; double min[3], max[3]; Mesh_GetGlobalCoordRange( self->mesh, min, max ); if ( self->count == self->size ) { self->size += self->delta; self->boundaries = Memory_Realloc_Array( self->boundaries, PeriodicBoundary, self->size ); } newPeriodicBoundary = &self->boundaries[self->count]; newPeriodicBoundary->axis = axis; newPeriodicBoundary->minWall = min[axis]; newPeriodicBoundary->maxWall = max[axis]; newPeriodicBoundary->particlesUpdatedMinEndCount = 0; newPeriodicBoundary->particlesUpdatedMaxEndCount = 0; self->count++; }
double _Byerlee_GetYieldCriterion( void* rheology, ConstitutiveMatrix* constitutiveMatrix, MaterialPointsSwarm* materialPointsSwarm, Element_LocalIndex lElement_I, MaterialPoint* materialPoint, Coord xi ) { Byerlee* self = (Byerlee*) rheology; double depth = 0.0; double height; int err; Coord coord; /* stupid way to recover the integration point from the materialPoint * TODO: FIXCONSTITUTIVE use integration point only */ IntegrationPoint* integrationPoint = (IntegrationPoint*) Swarm_ParticleAt( constitutiveMatrix->integrationSwarm, constitutiveMatrix->currentParticleIndex ); /* Calculate Depth */ if( self->height_id != -1 ) { err = PpcManager_Get( self->mgr, lElement_I, integrationPoint, self->height_id, &height ); Journal_Firewall( !err, global_error_stream, "Error in file %s. Cannot get reference height for '%s'. Ensure you have passed in the correct input with a line like\n\t<param name=\"HeightReferenceInput\">someInputfunction</param>\n", __func__, self->name ); } else { double min[3], max[3]; Mesh_GetGlobalCoordRange( self->mesh, min, max ); height = max[ J_AXIS ]; } /* This rheology assumes particle is an integration points thats can be mapped to a particle * that has no meaningful coord. Best to re-calc the global from local */ FeMesh_CoordLocalToGlobal( self->mesh, lElement_I, xi, coord ); depth = height - coord[ J_AXIS ]; return self->cohesion + self->depthCoefficient * depth; }
void _lucIsosurfaceCrossSection_Draw( void* drawingObject, lucDatabase* database, void* _context ) { lucIsosurfaceCrossSection* self = (lucIsosurfaceCrossSection*)drawingObject; lucIsosurface* isosurf = self->isosurface; double minIsovalue = self->minIsovalue; double maxIsovalue = self->maxIsovalue; lucColourMap* colourMap = self->colourMap; int i, j, k; double isovalue; Index triangle_I; /* Custom max, min, use global min/max if equal (defaults to both zero) */ if (minIsovalue == maxIsovalue) { //minIsovalue = FieldVariable_GetMinGlobalFieldMagnitude(self->fieldVariable); //maxIsovalue = FieldVariable_GetMaxGlobalFieldMagnitude(self->fieldVariable); } if (colourMap) lucColourMap_SetMinMax(colourMap, minIsovalue, maxIsovalue); /* Copy object id */ isosurf->id = self->id; if (self->rank == 0) { /* Calculate a value we can use to offset each surface section slightly * so they appear in the same position but don't actually overlap */ Coord min, max; float shift = 0; float range = 0; int d; Mesh_GetGlobalCoordRange(self->mesh, min, max ); for (d=0; d<3; d++) range += (max[d] - min[d]) / 5000.0; range /= 3.0; /* Allocate Memory */ Vertex** points = Memory_Alloc_2DArray( Vertex , 8, 1, "array for marching squares"); /* Draw isovalues at each interval from min to max */ for ( isovalue = minIsovalue ; isovalue <= maxIsovalue ; isovalue += self->interval ) { float nshift[3] = {shift * self->normal[0], shift * self->normal[1], shift * self->normal[2]}; shift += range; isosurf->triangleCount = 0; /* Reset */ isosurf->colourMap = NULL; if ( colourMap ) lucColourMap_GetColourFromValue(colourMap, isovalue, &isosurf->colour, self->opacity); /* Run marching rectangles for this isovalue */ isosurf->isovalue = isovalue; for ( i = 0 ; i < self->resolutionA-1 ; i++ ) { for ( j = 0 ; j < self->resolutionB-1 ; j++ ) { /* Copy vertex */ for (k = 0; k<3; k++) { points[LEFT_BOTTOM]->pos[k] = self->vertices[i][j][k] + nshift[k]; points[RIGHT_BOTTOM]->pos[k] = self->vertices[i+1][j][k] + nshift[k]; points[LEFT_TOP]->pos[k] = self->vertices[i][j+1][k] + nshift[k]; points[RIGHT_TOP]->pos[k] = self->vertices[i+1][j+1][k] + nshift[k]; } /* Copy value */ points[LEFT_BOTTOM]->value = self->values[i][j][0]; points[RIGHT_BOTTOM]->value = self->values[i+1][j][0]; points[LEFT_TOP]->value = self->values[i][j+1][0]; points[RIGHT_TOP]->value = self->values[i+1][j+1][0]; /* Interpolate mid-points and create triangles */ lucIsosurface_WallElement( isosurf, points ); } } /* Draw the surface section */ isosurf->colourMap = self->colourMap; _lucIsosurface_Draw(self->isosurface, database, _context ); /* Export colour values */ if (self->colourMap) { float iso = isovalue; for ( triangle_I = 0 ; triangle_I < isosurf->triangleCount ; triangle_I++) for (i=0; i<3; i++) lucDatabase_AddValues(database, 1, lucTriangleType, lucColourValueData, self->colourMap, &iso); } } Memory_Free( points ); } /* Free memory */ lucCrossSection_FreeSampleData(self); }
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 ); }
void lucMeshCrossSection_Sample( void* drawingObject, Bool reverse) { lucMeshCrossSection* self = (lucMeshCrossSection*)drawingObject; FeVariable* fieldVariable = (FeVariable*) NULL; // JM need to fix this guy assert(fieldVariable); Mesh* mesh = (Mesh*) fieldVariable->feMesh; Grid* vertGrid; Node_LocalIndex crossSection_I; IJK node_ijk; Node_GlobalIndex node_gI; Node_DomainIndex node_dI; int i,j, d, sizes[3] = {1,1,1}; Coord globalMin, globalMax, min, max; int localcount = 0; vertGrid = *(Grid**)ExtensionManager_Get( mesh->info, mesh, self->vertexGridHandle ); for (d=0; d<fieldVariable->dim; d++) sizes[d] = vertGrid->sizes[d]; self->dims[0] = sizes[ self->axis ]; self->dims[1] = sizes[ self->axis1 ]; self->dims[2] = sizes[ self->axis2 ]; crossSection_I = lucCrossSection_GetValue(self, 0, self->dims[0]-1); Mesh_GetLocalCoordRange(self->mesh, min, max ); Mesh_GetGlobalCoordRange(self->mesh, globalMin, globalMax ); Journal_Printf( lucDebug, "%s called on field %s, with axis of cross section as %d, crossSection_I as %d (dims %d,%d,%d) field dim %d\n", __func__, fieldVariable->name, self->axis, crossSection_I, self->dims[0], self->dims[1], self->dims[2], self->fieldDim); /* Get mesh cross section self->vertices and values */ self->resolutionA = self->dims[1]; self->resolutionB = self->dims[2]; lucCrossSection_AllocateSampleData(self, self->fieldDim); int lSize = Mesh_GetLocalSize( mesh, MT_VERTEX ); double time = MPI_Wtime(); Journal_Printf(lucInfo, "Sampling mesh (%s) %d x %d... 0%", self->name, self->dims[1], self->dims[2]); node_ijk[ self->axis ] = crossSection_I; for ( i = 0 ; i < self->dims[1]; i++ ) { int percent = 100 * (i + 1) / self->dims[1]; Journal_Printf(lucInfo, "\b\b\b\b%3d%%", percent); fflush(stdout); /* Reverse order if requested */ int i0 = i; if (reverse) i0 = self->dims[1] - i - 1; node_ijk[ self->axis1 ] = i0; for ( j = 0 ; j < self->dims[2]; j++ ) { self->vertices[i][j][0] = HUGE_VAL; self->vertices[i][j][2] = 0; node_ijk[ self->axis2 ] = j; node_gI = Grid_Project( vertGrid, node_ijk ); /* Get coord and value if node is local... */ if (Mesh_GlobalToDomain( mesh, MT_VERTEX, node_gI, &node_dI ) && node_dI < lSize) { /* Found on this processor */ double value[self->fieldDim]; FeVariable_GetValueAtNode( fieldVariable, node_dI, value ); double* pos = Mesh_GetVertex( mesh, node_dI ); /*fprintf(stderr, "[%d] (%d,%d) Node %d %f,%f,%f value %f\n", self->rank, i, j, node_gI, pos[0], pos[1], pos[2], value);*/ for (d=0; d<fieldVariable->dim; d++) self->vertices[i][j][d] = pos[d]; for (d=0; d<self->fieldDim; d++) self->values[i][j][d] = (float)value[d]; localcount++; } } } Journal_Printf(lucInfo, " %f sec. ", MPI_Wtime() - time); /* Collate */ time = MPI_Wtime(); for ( i=0 ; i < self->dims[1]; i++ ) { for ( j=0 ; j < self->dims[2]; j++ ) { /* Receive values at root */ if (self->rank == 0) { /* Already have value? */ if (self->vertices[i][j][0] != HUGE_VAL) {localcount--; continue; } /* Recv (pos and value together = (3 + fevar dims)*float) */ float data[3 + self->fieldDim]; (void)MPI_Recv(data, 3+self->fieldDim, MPI_FLOAT, MPI_ANY_SOURCE, i*self->dims[2]+j, self->comm, MPI_STATUS_IGNORE); /* Copy */ memcpy(self->vertices[i][j], data, 3 * sizeof(float)); memcpy(self->values[i][j], &data[3], self->fieldDim * sizeof(float)); } else { /* Found on this proc? */ if (self->vertices[i][j][0] == HUGE_VAL) continue; /* Copy */ float data[3 + self->fieldDim]; memcpy(data, self->vertices[i][j], 3 * sizeof(float)); memcpy(&data[3], self->values[i][j], self->fieldDim * sizeof(float)); /* Send values to root (pos & value = 4 * float) */ MPI_Ssend(data, 3+self->fieldDim, MPI_FLOAT, 0, i*self->dims[2]+j, self->comm); localcount--; } } } MPI_Barrier(self->comm); /* Barrier required, prevent subsequent MPI calls from interfering with transfer */ Journal_Printf(lucInfo, " Gather in %f sec.\n", MPI_Wtime() - time); Journal_Firewall(localcount == 0, lucError, "Error - in %s: count of values sampled compared to sent/received by mpi on proc %d does not match (balance = %d)\n", __func__, self->rank, localcount); }
void ParticleMelting_MeltFrationUpdate( ParticleMelting *self, void *data ) { MaterialPoint *mat_part=NULL; ExtensionManager *part_extMgr=NULL; MaterialPointsSwarm *mat_swarm=NULL; ParticleMelting_ParExt *parExt=NULL; ParticleMelting_MatExt *matExt=NULL; FeVariable *temperatureField=self->temperatureField; FeVariable *pressureField=self->pressureField; FeMesh* mesh = temperatureField->feMesh; Scaling *scaling=self->scaling; double depth, min[3], max[3]; double temperature, pressure, deepPressure, theta; unsigned lpartCount, par_i, part_extHandle, mat_extHandle; Mesh_GetGlobalCoordRange( mesh, min, max ); mat_swarm = self->materialSwarm; part_extMgr = mat_swarm->particleExtensionMgr; lpartCount = self->materialSwarm->particleLocalCount; part_extHandle = self->particleExtHandle; mat_extHandle = self->materialExtHandle; for( par_i = 0 ; par_i < lpartCount ; par_i++ ) { mat_part = (MaterialPoint*)Swarm_ParticleAt( mat_swarm, par_i ); /* get the particle extension */ parExt = ExtensionManager_Get( part_extMgr, mat_part, part_extHandle ); /* get the material extension */ matExt = MaterialPointsSwarm_GetMaterialExtensionOn( mat_swarm, mat_part, mat_extHandle ); if( !matExt->TurnMeltOn ) continue; /* Get temperature*/ _FeVariable_InterpolateValueAt( temperatureField, mat_part->coord, &temperature ); /* Get pressure*/ if( pressureField ) _FeVariable_InterpolateValueAt( pressureField, mat_part->coord, &pressure ); else { depth = fabs( max[J_AXIS] - mat_part->coord[J_AXIS] ); pressure = depth * self->refRho * self->gravity; } /* Get deep pressure */ deepPressure = matExt->deepPressure; /* Scale all */ if( self->scaling ) { temperature = Scaling_Unscale( scaling, temperature, sTemperature ); pressure = Scaling_Unscale( scaling, pressure, sPressure ); /* Safety checks - TODO: put some warnings here */ if( temperature < 0 ) temperature = 0; if( pressure < 0 ) pressure = 0; /*deepPressure = Scaling_Unscale( scaling, matExt->deepPressure, sPressure );*/ /* pressure in GPa */ pressure /= 1e9; deepPressure /= 1e9; } /* Calculate Depletion */ parExt->prevF = parExt->F; if( pressure < deepPressure ) theta = ParticleMelting_CalculateSuperSolidusTemperature( pressure, temperature, matExt->sCoeff, matExt->lCoeff ); else theta = ParticleMelting_CalculateSuperSolidusTemperature( pressure, temperature, matExt->sCoeffDeep, matExt->lCoeffDeep ); if( self->extractMelt == True ) { /* F is depletion so it cannot decrease! * Once some melt has been produced and removed, * the residual rock is depleted forever and ever, amen */ if( theta > parExt->maxTheta ) { parExt->F = ParticleMelting_CalculateMeltFraction( theta ); parExt->melt = parExt->F - ParticleMelting_CalculateMeltFraction( parExt->maxTheta ); parExt->maxTheta = theta; } else { parExt->melt = 0; } } else { /* melt isn't extracted so depletion can decrease */ parExt->F = ParticleMelting_CalculateMeltFraction( theta ); parExt->melt = parExt->F - parExt->prevF; } } }