void AggregateClusters(uint uParentLayer) { // number of cells in each grid cluster uint[] pClusterDims = mInfluenceTree.GetDecimations((int)uParentLayer); uint[] numCells = { mInfluenceTree[uParentLayer].GetNumCells(0), mInfluenceTree[uParentLayer].GetNumCells(1), mInfluenceTree[uParentLayer].GetNumCells(2) }; uint numXY = mInfluenceTree[uParentLayer].GetNumPoints(0) * mInfluenceTree[uParentLayer].GetNumPoints(1); // (Since this loop writes to each parent cell, it should readily parallelize without contention.) uint[] idxParent = new uint[3]; for (idxParent[2] = 0; idxParent[2] < numCells[2]; ++idxParent[2]) { uint offsetZ = idxParent[2] * numXY; for (idxParent[1] = 0; idxParent[1] < numCells[1]; ++idxParent[1]) { uint offsetYZ = idxParent[1] * mInfluenceTree[uParentLayer].GetNumPoints(0) + offsetZ; for (idxParent[0] = 0; idxParent[0] < numCells[0]; ++idxParent[0]) { // For each cell in the parent layer... uint offsetXYZ = idxParent[0] + offsetYZ; UniformGrid <Vorton> rChildLayer = mInfluenceTree[uParentLayer - 1]; VortonClusterAux vortAux = new VortonClusterAux(); uint[] clusterMinIndices = mInfluenceTree.GetChildClusterMinCornerIndex(pClusterDims, idxParent);; uint[] increment = { 0, 0, 0 }; uint numXchild = rChildLayer.GetNumPoints(0); uint numXYchild = numXchild * rChildLayer.GetNumPoints(1); // For each cell of child layer in this grid cluster... for (increment[2] = 0; increment[2] < pClusterDims[2]; ++increment[2]) { uint childOffsetZ = (clusterMinIndices[2] + increment[2]) * numXYchild; for (increment[1] = 0; increment[1] < pClusterDims[1]; ++increment[1]) { uint childOffsetYZ = (clusterMinIndices[1] + increment[1]) * numXchild + childOffsetZ; for (increment[0] = 0; increment[0] < pClusterDims[0]; ++increment[0]) { uint childOffsetXYZ = (clusterMinIndices[0] + increment[0]) + childOffsetYZ; Vorton rVortonChild = rChildLayer[childOffsetXYZ]; float vortMag = rVortonChild.vorticity.magnitude; // Aggregate vorton cluster from child layer into parent layer: mInfluenceTree[uParentLayer][offsetXYZ].position += rVortonChild.position * vortMag; mInfluenceTree[uParentLayer][offsetXYZ].vorticity += rVortonChild.vorticity; vortAux.mVortNormSum += vortMag; if (rVortonChild.radius != 0.0f) { mInfluenceTree[uParentLayer][offsetXYZ].radius = rVortonChild.radius; } } } } // Normalize weighted position sum to obtain center-of-vorticity. // (See analogous code in MakeBaseVortonGrid.) mInfluenceTree[uParentLayer][offsetXYZ].position /= vortAux.mVortNormSum; } } } }
/* * Advect vortons using velocity field * * timeStep - amount of time by which to advance simulation * */ void AdvectVortons(float timeStep) { int numVortons = mVortons.Count; for (int offset = 0; offset < numVortons; ++offset) { // For each vorton... Vorton rVorton = mVortons[offset]; Vector3 velocity = ((Vector)mVelGrid.Interpolate(rVorton.position)).v; mVortons[offset].position += velocity * timeStep; mVortons[offset].velocity = velocity; // Cache this for use in collisions with rigid bodies. } }
/* * Computes the total circulation and linear impulse of all vortons in this simulation. * * vCirculation - Total circulation, the volume integral of the vorticity. * vLinearImpulse - Volume integral of circulation weighted by position. */ void ConservedQuantities() { // Zero accumulators. mCirculationInitial = mLinearImpulseInitial = new Vector3(0.0f, 0.0f, 0.0f); int numVortons = mVortons.Count; for (int iVorton = 0; iVorton < numVortons; ++iVorton) { // For each vorton in this simulation... Vorton Vorton = mVortons[iVorton]; float volumeElement = (mVortons[iVorton].radius * mVortons[iVorton].radius * mVortons[iVorton].radius) * 8.0f; // Accumulate total circulation. mCirculationInitial += mVortons[iVorton].vorticity * volumeElement; // Accumulate total linear impulse. mLinearImpulseInitial += Vector3.Cross(mVortons[iVorton].position, mVortons[iVorton].vorticity * volumeElement); } }
void AssignVorticity(float fMagnitude, uint numVortonsMax, IVorticityDistribution vorticityDistribution) { Vector3 vDimensions = vorticityDistribution.GetDomainSize(); // length of each side of grid box Vector3 vCenter = (vorticityDistribution).GetCenter(); // Center of vorticity distribution Vector3 vMin = (vCenter - 0.5f * vDimensions); // Minimum corner of box containing vortons Vector3 vMax = (vMin + vDimensions); // Maximum corner of box containing vortons UniformGridGeometry skeleton = new UniformGridGeometry(numVortonsMax, vMin, vMax, true); // number of grid cells in each direction of virtual uniform grid int[] numCells = { (int)Mathf.Max(1, skeleton.GetNumCells(0)) , (int)Mathf.Max(1, skeleton.GetNumCells(1)) , (int)Mathf.Max(1, skeleton.GetNumCells(2)) }; // Total number of cells should be as close to numVortonsMax as possible without going over. // Worst case allowable difference would be numVortonsMax=7 and numCells in each direction is 1 which yields a ratio of 1/7. // But in typical situations, the user would like expect total number of virtual cells to be closer to numVortonsMax than that. // E.g. if numVortonsMax=8^3=512 somehow yielded numCells[0]=numCells[1]=numCells[2]=7 then the ratio would be 343/512~=0.67. while (numCells[0] * numCells[1] * numCells[2] > numVortonsMax) { // Number of cells is excessive. // This can happen when the trial number of cells in any direction is less than 1 -- then the other two will likely be too large. numCells[0] = (int)Mathf.Max(1, numCells[0] / 2); numCells[1] = (int)Mathf.Max(1, numCells[1] / 2); numCells[2] = (int)Mathf.Max(1, numCells[2] / 2); } float[] oneOverN = { 1.0f / (float)(numCells[0]), 1.0f / (float)(numCells[1]), 1.0f / (float)(numCells[2]) }; Vector3 gridCellSize = new Vector3(vDimensions.x * oneOverN[0], vDimensions.y * oneOverN[1], vDimensions.z * oneOverN[2]); float vortonRadius = Mathf.Pow(gridCellSize.x * gridCellSize.y * gridCellSize.z, 1.0f / 3.0f) * 0.5f; if (0.0f == vDimensions.z) { // z size is zero, so domain is 2D. vortonRadius = Mathf.Pow(gridCellSize.x * gridCellSize.y, 0.5f) * 0.5f; } Vector3 vNoise = (0.0f * gridCellSize); //----------------------------------------------------------------------- // Iterate through each point in a uniform grid. // If probe position is inside vortex core, add a vorton there. // This loop could be rewritten such that it only visits points inside the core, // but this loop structure can readily be reused for a wide variety of configurations. Vector3 position = new Vector3(0.0f, 0.0f, 0.0f); // vorton position int[] index = new int[3]; // index of each position visited for (index[2] = 0; index[2] < numCells[2]; ++index[2]) { // For each z-coordinate... position.z = ((float)(index[2]) + 0.25f) * gridCellSize.z + vMin.z; for (index[1] = 0; index[1] < numCells[1]; ++index[1]) { // For each y-coordinate... position.y = ((float)(index[1]) + 0.25f) * gridCellSize.y + vMin.y; for (index[0] = 0; index[0] < numCells[0]; ++index[0]) { // For each x-coordinate... position.x = ((float)(index[0]) + 0.25f) * gridCellSize.x + vMin.x; position += Random.Range(-1, 1) * (vNoise); Vector3 vorticity = Vector3.zero; vorticityDistribution.AssignVorticity(ref vorticity, position, vCenter); Vorton vorton = new Vorton(position, vorticity * fMagnitude, vortonRadius); if (vorticity.sqrMagnitude > global.GlobalVar.sTiny) { // Vorticity is significantly non-zero. mVortonSim.AddVorton(vorton); } } } } //----------------------------------------------------------------------- }
/* * Diffuse vorticity using a particle strength exchange method. * * This routine partitions space into cells using the same grid * as the "base vorton" grid. Each vorton gets assigned to the * cell that contains it. Then, each vorton exchanges some * of its vorticity with its neighbors in adjacent cells. * * This routine makes some simplifying assumptions to speed execution: * * - Distance does not influence the amount of vorticity exchanged, * except in as much as only vortons within a certain region of * each other exchange vorticity. This amounts to saying our kernel, * eta, is a top-hat function. * * - Theoretically, if an adjacent cell contains no vortons * then this simulation should generate vorticity within * that cell, e.g. by creating a new vorton in the adjacent cell. * * - This simulation reduces the vorticity of each vorton, alleging * that this vorticity is dissipated analogously to how energy * dissipates at Kolmogorov microscales. This treatment is not * realistic but it retains qualitative characteristics that we * want, e.g. that the flow dissipates at a rate related to viscosity. * Dissipation in real flows is a more complicated phenomenon. * * see Degond & Mas-Gallic (1989): The weighted particle method for * convection-diffusion equations, part 1: the case of an isotropic viscosity. * Math. Comput., v. 53, n. 188, pp. 485-507, October. * * timeStep - amount of time by which to advance simulation * * This routine assumes CreateInfluenceTree has already executed. * */ void DiffuseVorticityPSE(float timeStep) { // Phase 1: Partition vortons // Create a spatial partition for the vortons. // Each cell contains a dynamic array of integers // whose values are offsets into mVortons. UniformGrid <IntList> ugVortRef = new UniformGrid <IntList>(mInfluenceTree[0]); ugVortRef.Init(); int numVortons = mVortons.Count; for (int offset = 0 /* Start at 0th vorton */; offset < numVortons; ++offset) { // For each vorton... // Insert the vorton's offset into the spatial partition. ugVortRef[mVortons[offset].position].list.Add(offset); } // Phase 2: Exchange vorticity with nearest neighbors uint nx = ugVortRef.GetNumPoints(0); uint nxm1 = nx - 1; uint ny = ugVortRef.GetNumPoints(1); uint nym1 = ny - 1; uint nxy = nx * ny; uint nz = ugVortRef.GetNumPoints(2); uint nzm1 = nz - 1; uint[] idx = new uint[3]; for (idx[2] = 0; idx[2] < nzm1; ++idx[2]) { // For all points along z except the last... uint offsetZ0 = idx[2] * nxy; uint offsetZp = (idx[2] + 1) * nxy; for (idx[1] = 0; idx[1] < nym1; ++idx[1]) { // For all points along y except the last... uint offsetY0Z0 = idx[1] * nx + offsetZ0; uint offsetYpZ0 = (idx[1] + 1) * nx + offsetZ0; uint offsetY0Zp = idx[1] * nx + offsetZp; for (idx[0] = 0; idx[0] < nxm1; ++idx[0]) { // For all points along x except the last... uint offsetX0Y0Z0 = idx[0] + offsetY0Z0; for (int ivHere = 0; ivHere < ugVortRef[offsetX0Y0Z0].list.Count; ++ivHere) { // For each vorton in this gridcell... int rVortIdxHere = ugVortRef[offsetX0Y0Z0].list[ivHere]; Vorton rVortonHere = mVortons[rVortIdxHere]; Vector3 rVorticityHere = rVortonHere.vorticity; // Diffuse vorticity with other vortons in this same cell: for (int ivThere = ivHere + 1; ivThere < ugVortRef[offsetX0Y0Z0].list.Count; ++ivThere) { // For each OTHER vorton within this same cell... int rVortIdxThere = ugVortRef[offsetX0Y0Z0].list[ivThere]; Vorton rVortonThere = mVortons[rVortIdxThere]; Vector3 rVorticityThere = rVortonThere.vorticity; Vector3 vortDiff = rVorticityHere - rVorticityThere; Vector3 exchange = 2.0f * mViscosity * timeStep * vortDiff; // Amount of vorticity to exchange between particles. mVortons[rVortIdxHere].vorticity -= exchange; // Make "here" vorticity a little closer to "there". mVortons[rVortIdxThere].vorticity += exchange; // Make "there" vorticity a little closer to "here". } // Diffuse vorticity with vortons in adjacent cells: { uint offsetXpY0Z0 = idx[0] + 1 + offsetY0Z0; // offset of adjacent cell in +X direction for (int ivThere = 0; ivThere < ugVortRef[offsetXpY0Z0].list.Count; ++ivThere) { // For each vorton in the adjacent cell in +X direction... int rVortIdxThere = ugVortRef[offsetXpY0Z0].list[ivThere]; Vorton rVortonThere = mVortons[rVortIdxThere]; Vector3 rVorticityThere = rVortonThere.vorticity; Vector3 vortDiff = rVorticityHere - rVorticityThere; Vector3 exchange = mViscosity * timeStep * vortDiff; // Amount of vorticity to exchange between particles. mVortons[rVortIdxHere].vorticity -= exchange; // Make "here" vorticity a little closer to "there". mVortons[rVortIdxThere].vorticity += exchange; // Make "there" vorticity a little closer to "here". } } { uint offsetX0YpZ0 = idx[0] + offsetYpZ0; // offset of adjacent cell in +Y direction for (int ivThere = 0; ivThere < ugVortRef[offsetX0YpZ0].list.Count; ++ivThere) { // For each vorton in the adjacent cell in +Y direction... int rVortIdxThere = ugVortRef[offsetX0YpZ0].list[ivThere]; Vorton rVortonThere = mVortons[rVortIdxThere]; Vector3 rVorticityThere = rVortonThere.vorticity; Vector3 vortDiff = rVorticityHere - rVorticityThere; Vector3 exchange = mViscosity * timeStep * vortDiff; // Amount of vorticity to exchange between particles. mVortons[rVortIdxHere].vorticity -= exchange; // Make "here" vorticity a little closer to "there". mVortons[rVortIdxThere].vorticity += exchange; // Make "there" vorticity a little closer to "here". } } { uint offsetX0Y0Zp = idx[0] + offsetY0Zp; // offset of adjacent cell in +Z direction for (int ivThere = 0; ivThere < ugVortRef[offsetX0Y0Zp].list.Count; ++ivThere) { // For each vorton in the adjacent cell in +Z direction... int rVortIdxThere = ugVortRef[offsetX0Y0Zp].list[ivThere]; Vorton rVortonThere = mVortons[rVortIdxThere]; Vector3 rVorticityThere = rVortonThere.vorticity; Vector3 vortDiff = rVorticityHere - rVorticityThere; Vector3 exchange = mViscosity * timeStep * vortDiff; // Amount of vorticity to exchange between particles. mVortons[rVortIdxHere].vorticity -= exchange; // Make "here" vorticity a little closer to "there". mVortons[rVortIdxThere].vorticity += exchange; // Make "there" vorticity a little closer to "here". } } // Dissipate vorticity. See notes in header comment. mVortons[rVortIdxHere].vorticity -= mViscosity * timeStep * mVortons[rVortIdxHere].vorticity; // Reduce "here" vorticity. } } } } }
public void AddVorton(Vorton vorton) { mVortons.Add(vorton); }
public Vorton(Vorton that) { mPosition = that.mPosition; mVorticity = that.mVorticity; mRadius = that.mRadius; }
void AssignVorticity(float fMagnitude, uint numVortonsMax, IVorticityDistribution vorticityDistribution) { Vector3 vDimensions = vorticityDistribution.GetDomainSize(); // length of each side of grid box Vector3 vCenter = (vorticityDistribution).GetCenter(); // Center of vorticity distribution Vector3 vMin = (vCenter - 0.5f * vDimensions) ; // Minimum corner of box containing vortons Vector3 vMax = (vMin + vDimensions) ; // Maximum corner of box containing vortons UniformGridGeometry skeleton = new UniformGridGeometry(numVortonsMax, vMin, vMax, true ) ; // number of grid cells in each direction of virtual uniform grid int[] numCells = { (int)Mathf.Max( 1 , skeleton.GetNumCells(0)) , (int)Mathf.Max( 1 , skeleton.GetNumCells(1)) , (int)Mathf.Max( 1 , skeleton.GetNumCells(2)) }; // Total number of cells should be as close to numVortonsMax as possible without going over. // Worst case allowable difference would be numVortonsMax=7 and numCells in each direction is 1 which yields a ratio of 1/7. // But in typical situations, the user would like expect total number of virtual cells to be closer to numVortonsMax than that. // E.g. if numVortonsMax=8^3=512 somehow yielded numCells[0]=numCells[1]=numCells[2]=7 then the ratio would be 343/512~=0.67. while (numCells[0] * numCells[1] * numCells[2] > numVortonsMax) { // Number of cells is excessive. // This can happen when the trial number of cells in any direction is less than 1 -- then the other two will likely be too large. numCells[0] = (int)Mathf.Max(1, numCells[0] / 2); numCells[1] = (int)Mathf.Max(1, numCells[1] / 2); numCells[2] = (int)Mathf.Max(1, numCells[2] / 2); } float[] oneOverN = { 1.0f / (float)(numCells[0]), 1.0f / (float)(numCells[1]), 1.0f / (float)(numCells[2]) }; Vector3 gridCellSize = new Vector3(vDimensions.x * oneOverN[0] , vDimensions.y * oneOverN[1], vDimensions.z * oneOverN[2] ) ; float vortonRadius = Mathf.Pow(gridCellSize.x * gridCellSize.y * gridCellSize.z, 1.0f / 3.0f) * 0.5f; if (0.0f == vDimensions.z) { // z size is zero, so domain is 2D. vortonRadius = Mathf.Pow(gridCellSize.x * gridCellSize.y, 0.5f) * 0.5f; } Vector3 vNoise = (0.0f * gridCellSize) ; //----------------------------------------------------------------------- // Iterate through each point in a uniform grid. // If probe position is inside vortex core, add a vorton there. // This loop could be rewritten such that it only visits points inside the core, // but this loop structure can readily be reused for a wide variety of configurations. Vector3 position = new Vector3(0.0f, 0.0f, 0.0f); // vorton position int[] index = new int[3]; // index of each position visited for (index[2] = 0; index[2] < numCells[2]; ++index[2]) { // For each z-coordinate... position.z = ((float)(index[2]) + 0.25f) * gridCellSize.z + vMin.z; for (index[1] = 0; index[1] < numCells[1]; ++index[1]) { // For each y-coordinate... position.y = ((float)(index[1]) + 0.25f) * gridCellSize.y + vMin.y; for (index[0] = 0; index[0] < numCells[0]; ++index[0]) { // For each x-coordinate... position.x = ((float)(index[0]) + 0.25f) * gridCellSize.x + vMin.x; position += Random.Range(-1, 1) * (vNoise); Vector3 vorticity = Vector3.zero; vorticityDistribution.AssignVorticity(ref vorticity, position, vCenter); Vorton vorton = new Vorton(position, vorticity * fMagnitude, vortonRadius); if (vorticity.sqrMagnitude > global.GlobalVar.sTiny) { // Vorticity is significantly non-zero. mVortonSim.AddVorton(vorton); } } } } //----------------------------------------------------------------------- }
public Vorton( Vorton that ) { mPosition = that.mPosition; mVorticity = that.mVorticity; mRadius = that.mRadius; }