/// <summary> /// Sets the boundary cells at the outer edges of the cube so they perfectly counteract their neighbor /// </summary> /// <remarks> /// This is a way to keep fluid from leaking out of the box. (not having walls really screws up the simulation code). /// Walls are added by treating the outer layer of cells as the wall. Basically, every velocity in the layer next to this /// outer layer is mirrored. So when you have some velocity towards the wall in the next-to-outer layer, the wall /// gets a velocity that perfectly counters it. /// /// /// Say we're at the left edge of the cube. The left cell is the boundary cell that needs to counteract its neighbor, the /// right cell. The right cell has a velocity that's up and to the left. /// /// The boundary cell's x velocity needs to be opposite its neighbor, but its y velocity needs to be equal to its neighbor. /// This will produce a result that counteracts the motion of the fluid which would take it through the wall, and preserves /// the rest of the motion. /// /// So what action is taken depends on which array is passed in; if we're dealing with x velocities, then we have to set /// the boundary cell's value to the opposite of its neighbor, but for everything else we set it to be the same. /// /// /// This function also sets corners. This is done very simply, by setting each corner cell equal to the average of its three /// neighbors. /// </remarks> private static void SetBoundry_ORIG(SetBoundsType whichSide, double[] array, int size, BoundrySettings boundrySettings) { const double ONETHIRD = 1d / 3d; // Reflect the walls //NOTE: These arrays go from 1 to length-1. The corners are handled later // X wall for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { array[IX(0, y, z, size)] = whichSide == SetBoundsType.VelocityX ? -array[IX(1, y, z, size)] : array[IX(1, y, z, size)]; array[IX(size - 1, y, z, size)] = whichSide == SetBoundsType.VelocityX ? -array[IX(size - 2, y, z, size)] : array[IX(size - 2, y, z, size)]; } } // Y wall for (int z = 1; z < size - 1; z++) { for (int x = 1; x < size - 1; x++) { array[IX(x, 0, z, size)] = whichSide == SetBoundsType.VelocityY ? -array[IX(x, 1, z, size)] : array[IX(x, 1, z, size)]; array[IX(x, size - 1, z, size)] = whichSide == SetBoundsType.VelocityY ? -array[IX(x, size - 2, z, size)] : array[IX(x, size - 2, z, size)]; } } // Z wall for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { array[IX(x, y, 0, size)] = whichSide == SetBoundsType.VelocityZ ? -array[IX(x, y, 1, size)] : array[IX(x, y, 1, size)]; array[IX(x, y, size - 1, size)] = whichSide == SetBoundsType.VelocityZ ? -array[IX(x, y, size - 2, size)] : array[IX(x, y, size - 2, size)]; } } // Corners take average of neighbors array[IX(0, 0, 0, size)] = ONETHIRD * (array[IX(1, 0, 0, size)] + array[IX(0, 1, 0, size)] + array[IX(0, 0, 1, size)]); array[IX(0, size - 1, 0, size)] = ONETHIRD * (array[IX(1, size - 1, 0, size)] + array[IX(0, size - 2, 0, size)] + array[IX(0, size - 1, 1, size)]); array[IX(0, 0, size - 1, size)] = ONETHIRD * (array[IX(1, 0, size - 1, size)] + array[IX(0, 1, size - 1, size)] + array[IX(0, 0, size - 2, size)]); array[IX(0, size - 1, size - 1, size)] = ONETHIRD * (array[IX(1, size - 1, size - 1, size)] + array[IX(0, size - 2, size - 1, size)] + array[IX(0, size - 1, size - 2, size)]); array[IX(size - 1, 0, 0, size)] = ONETHIRD * (array[IX(size - 2, 0, 0, size)] + array[IX(size - 1, 1, 0, size)] + array[IX(size - 1, 0, 1, size)]); array[IX(size - 1, size - 1, 0, size)] = ONETHIRD * (array[IX(size - 2, size - 1, 0, size)] + array[IX(size - 1, size - 2, 0, size)] + array[IX(size - 1, size - 1, 1, size)]); array[IX(size - 1, 0, size - 1, size)] = ONETHIRD * (array[IX(size - 2, 0, size - 1, size)] + array[IX(size - 1, 1, size - 1, size)] + array[IX(size - 1, 0, size - 2, size)]); array[IX(size - 1, size - 1, size - 1, size)] = ONETHIRD * (array[IX(size - 2, size - 1, size - 1, size)] + array[IX(size - 1, size - 2, size - 1, size)] + array[IX(size - 1, size - 1, size - 2, size)]); }
/// <remarks> /// Every cell has a set of velocities, and these velocities make things move. This is called advection. As with diffusion, advection /// applies both to the dye and to the velocities themselves. /// /// This function is responsible for actually moving things around. To that end, it looks at each cell in turn. In that cell, it grabs the /// velocity, follows that velocity back in time, and sees where it lands. It then takes a weighted average of the cells around the spot /// where it lands, then applies that value to the current cell. /// </remarks> private static void Advect(SetBoundsType whichSide, double[] dest, double[] source, double[] velocX, double[] velocY, double[] velocZ, bool[] blocked, double timestep, int size, BoundrySettings boundrySettings) { for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { int index1D = IX(x, y, z, size); if (blocked[index1D]) { continue; } // Reverse velocity, since we are interpolating backwards // xSrc and ySrc is the position of the source density. double xSrc = x - (timestep * velocX[index1D]); double ySrc = y - (timestep * velocY[index1D]); double zSrc = z - (timestep * velocZ[index1D]); if (xSrc < 0.5d) xSrc = 0.5d; if (xSrc > size - 1.5d) xSrc = size - 1.5d; int x0 = (int)xSrc; // casting to int is also the equivalent of Math.Floor int x1 = x0 + 1; if (ySrc < 0.5d) ySrc = 0.5d; if (ySrc > size - 1.5d) ySrc = size - 1.5d; int y0 = (int)ySrc; int y1 = y0 + 1; if (zSrc < 0.5d) zSrc = 0.5d; if (zSrc > size - 1.5d) zSrc = size - 1.5d; int z0 = (int)zSrc; int z1 = z0 + 1; //Linear interpolation factors. Ex: 0.6 and 0.4 double xProp1 = xSrc - x0; double xProp0 = 1d - xProp1; double yProp1 = ySrc - y0; double yProp0 = 1d - yProp1; double zProp1 = zSrc - z0; double zProp0 = 1d - zProp1; // ugly no matter how you nest it dest[index1D] = xProp0 * (yProp0 * (zProp0 * source[IX(x0, y0, z0, size)] + zProp1 * source[IX(x0, y0, z1, size)]) + (yProp1 * (zProp0 * source[IX(x0, y1, z0, size)] + zProp1 * source[IX(x0, y1, z1, size)]))) + xProp1 * (yProp0 * (zProp0 * source[IX(x1, y0, z0, size)] + zProp1 * source[IX(x1, y0, z1, size)]) + (yProp1 * (zProp0 * source[IX(x1, y1, z0, size)] + zProp1 * source[IX(x1, y1, z1, size)]))); } } } SetBoundry(whichSide, dest, blocked, size, boundrySettings); }
/// <summary> /// Not exactly sure how this method works, it's solving a linear differential equation of some sort /// </summary> /// <remarks> /// This runs through the whole array and sets each cell to a combination of its neighbors. It does this several times; the more /// iterations it does, the more accurate the results, but the slower things run. (4 iterations is a good number to use). /// /// After each iteration, it resets the boundaries so the calculations don't explode. /// </remarks> private static void LinearSolve(SetBoundsType whichSide, double[] dest, double[] source, bool[] blocked, double a, double c, int iterations, int size, BoundrySettings boundrySettings) { double cRecip = 1d / c; int index; for (int cntr = 0; cntr < iterations; cntr++) { for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { index = IX(x, y, z, size); if (blocked[index]) { continue; } dest[index] = (source[IX(x, y, z, size)] + a * (dest[IX(x + 1, y, z, size)] + dest[IX(x - 1, y, z, size)] + dest[IX(x, y + 1, z, size)] + dest[IX(x, y - 1, z, size)] + dest[IX(x, y, z + 1, size)] + dest[IX(x, y, z - 1, size)] )) * cRecip; } } } SetBoundry(whichSide, dest, blocked, size, boundrySettings); } }
/// <summary> /// Allows the fluid to spread out (blurs the fluid) /// </summary> /// <remarks> /// Put a drop of soy sauce in some water, and you'll notice that it doesn't stay still, but it spreads out. This /// happens even if the water and sauce are both perfectly still. This is called diffusion. We use diffusion both /// in the obvious case of making the dye spread out, and also in the less obvious case of making the velocities /// of the fluid spread out. /// /// Diffuse is really simple; it just precalculates a value and passes everything off to lin_solve. So that means, /// while I know what it does, I don't really know how, since all the work is in that mysterious function /// </remarks> private static void Diffuse(SetBoundsType whichSide, double[] destination, double[] source, bool[] blocked, double timestep, double diffusion, int iterations, int size, BoundrySettings boundrySettings) { double a = timestep * diffusion; //NOTE: The 2D uses 4. If 4 is used here, the color just spreads out, so maybe it's 2xDimensions? LinearSolve(whichSide, destination, source, blocked, a, 1 + 6 * a, iterations, size, boundrySettings); }
/// <summary> /// Forces the velocity to be mass conserving /// </summary> /// <remarks> /// The fluid that this class models is incompressible This means that the amount of fluid in each box has to stay constant. /// That means that the amount of fluid going in has to be exactly equal to the amount of fluid going out. The other /// operations tend to screw things up so that you get some boxes with a net outflow, and some with a net inflow. This /// operation runs through all the cells and fixes them up so everything is in equilibrium. /// /// This function is also somewhat mysterious as to exactly how it works, but it does some more running through the data /// and setting values, with some calls to lin_solve thrown in for fun /// /// -------------- /// /// Every velocity field is the sum of an incompressible field and a gradient field. To obtain an incompressible field we /// simply subtract the gradient field from our current velocities. /// /// The gradient field indicates the direction of steepest descent of some height function. Imagine a terrain with hills and /// valleys with an arrow at every point pointing in the direction of steepest descent /// </remarks> private static void Project(double[] velocX, double[] velocY, double[] velocZ, double[] p, double[] div, bool[] blocked, int iterations, int size, BoundrySettings boundrySettings) { int index; for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { index = IX(x, y, z, size); if (blocked[index]) { continue; } //Negative divergence div[index] = -0.5d * ( velocX[IX(x + 1, y, z, size)] - velocX[IX(x - 1, y, z, size)] + velocY[IX(x, y + 1, z, size)] - velocY[IX(x, y - 1, z, size)] + velocZ[IX(x, y, z + 1, size)] - velocZ[IX(x, y, z - 1, size)] ) / size; //Pressure field p[index] = 0; } } } SetBoundry(SetBoundsType.Other, div, blocked, size, boundrySettings); SetBoundry(SetBoundsType.Other, p, blocked, size, boundrySettings); LinearSolve(SetBoundsType.Other, p, div, blocked, 1, 6, iterations, size, boundrySettings); for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { index = IX(x, y, z, size); if (blocked[index]) { continue; } velocX[index] -= 0.5d * (p[IX(x + 1, y, z, size)] - p[IX(x - 1, y, z, size)]) * size; velocY[index] -= 0.5d * (p[IX(x, y + 1, z, size)] - p[IX(x, y - 1, z, size)]) * size; velocZ[index] -= 0.5d * (p[IX(x, y, z + 1, size)] - p[IX(x, y, z - 1, size)]) * size; } } } SetBoundry(SetBoundsType.VelocityX, velocX, blocked, size, boundrySettings); SetBoundry(SetBoundsType.VelocityY, velocY, blocked, size, boundrySettings); SetBoundry(SetBoundsType.VelocityZ, velocZ, blocked, size, boundrySettings); }
private static void SetBoundry_ReachAround(SetBoundsType whichSide, double[] cells, int size, BoundrySettings boundrySettings) { double average, sum, count; bool wrapX = whichSide == SetBoundsType.VelocityX || whichSide == SetBoundsType.Ink; bool wrapY = whichSide == SetBoundsType.VelocityY || whichSide == SetBoundsType.Ink; bool wrapZ = whichSide == SetBoundsType.VelocityZ || whichSide == SetBoundsType.Ink; #region X wall if (wrapX) { // Copy of other side for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { cells[IX(0, y, z, size)] = cells[IX(size - 2, y, z, size)]; cells[IX(size - 1, y, z, size)] = cells[IX(1, y, z, size)]; } } } else { // Average for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { average = .5d * (cells[IX(1, y, z, size)] + cells[IX(size - 2, y, z, size)]); cells[IX(0, y, z, size)] = average; cells[IX(size - 1, y, z, size)] = average; } } } #endregion #region Y wall if (wrapY) { // Copy of other side for (int z = 1; z < size - 1; z++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, 0, z, size)] = cells[IX(x, size - 2, z, size)]; cells[IX(x, size - 1, z, size)] = cells[IX(x, 1, z, size)]; } } } else { // Average for (int z = 1; z < size - 1; z++) { for (int x = 1; x < size - 1; x++) { average = .5d * (cells[IX(x, 1, z, size)] + cells[IX(x, size - 2, z, size)]); cells[IX(x, 0, z, size)] = average; cells[IX(x, size - 1, z, size)] = average; } } } #endregion #region Z wall if (wrapZ) { // Copy of other side for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, y, 0, size)] = cells[IX(x, y, size - 2, size)]; cells[IX(x, y, size - 1, size)] = cells[IX(x, y, 1, size)]; } } } else { // Average for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { average = .5d * (cells[IX(x, y, 1, size)] + cells[IX(x, y, size - 2, size)]); cells[IX(x, y, 0, size)] = average; cells[IX(x, y, size - 1, size)] = average; } } } #endregion #region X edges for (int x = 1; x < size - 1; x++) { // 1 sum = 0; count = 0; sum += cells[IX(x, size - 2, 0, size)]; count++; if (!wrapY) { sum += cells[IX(x, 1, 0, size)]; count++; } sum += cells[IX(x, 0, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(x, 0, 1, size)]; count++; } cells[IX(x, 0, 0, size)] = sum / count; // 2 sum = 0; count = 0; sum += cells[IX(x, 1, 0, size)]; count++; if (!wrapY) { sum += cells[IX(x, size - 2, 0, size)]; count++; } sum += cells[IX(x, size - 1, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(x, size - 1, 1, size)]; count++; } cells[IX(x, size - 1, 0, size)] = sum / count; // 3 sum = 0; count = 0; sum += cells[IX(x, size - 2, size - 1, size)]; count++; if (!wrapY) { sum += cells[IX(x, 1, size - 1, size)]; count++; } sum += cells[IX(x, 0, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(x, 0, size - 2, size)]; count++; } cells[IX(x, 0, size - 1, size)] = sum / count; // 4 sum = 0; count = 0; sum += cells[IX(x, 1, size - 1, size)]; count++; if (!wrapY) { sum += cells[IX(x, size - 2, size - 1, size)]; count++; } sum += cells[IX(x, size - 1, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(x, size - 1, size - 2, size)]; count++; } cells[IX(x, size - 1, size - 1, size)] = sum / count; } #endregion #region Y edges for (int y = 1; y < size - 1; y++) { // 1 sum = 0; count = 0; sum += cells[IX(size - 2, y, 0, size)]; count++; if (!wrapX) { sum += cells[IX(1, y, 0, size)]; count++; } sum += cells[IX(0, y, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(0, y, 1, size)]; count++; } cells[IX(0, y, 0, size)] = sum / count; // 2 sum = 0; count = 0; sum += cells[IX(1, y, 0, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, y, 0, size)]; count++; } sum += cells[IX(size - 1, y, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(size - 1, y, 1, size)]; count++; } cells[IX(size - 1, y, 0, size)] = sum / count; // 3 sum = 0; count = 0; sum += cells[IX(size - 2, y, size - 1, size)]; count++; if (!wrapX) { sum += cells[IX(1, y, size - 1, size)]; count++; } sum += cells[IX(0, y, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(0, y, size - 2, size)]; count++; } cells[IX(0, y, size - 1, size)] = sum / count; // 4 sum = 0; count = 0; sum += cells[IX(1, y, size - 1, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, y, size - 1, size)]; count++; } sum += cells[IX(size - 1, y, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(size - 1, y, size - 2, size)]; count++; } cells[IX(size - 1, y, size - 1, size)] = sum / count; } #endregion #region Z edges for (int z = 0; z < size - 1; z++) { // 1 sum = 0; count = 0; sum += cells[IX(size - 2, 0, z, size)]; count++; if (!wrapX) { sum += cells[IX(1, 0, z, size)]; count++; } sum += cells[IX(0, size - 2, z, size)]; count++; if (!wrapY) { sum += cells[IX(0, 1, z, size)]; count++; } cells[IX(0, 0, z, size)] = sum / count; // 2 sum = 0; count = 0; sum += cells[IX(1, 0, z, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, 0, z, size)]; count++; } sum += cells[IX(size - 1, size - 2, z, size)]; count++; if (!wrapY) { sum += cells[IX(size - 1, 1, z, size)]; count++; } cells[IX(size - 1, 0, z, size)] = sum / count; // 3 sum = 0; count = 0; sum += cells[IX(size - 2, size - 1, z, size)]; count++; if (!wrapX) { sum += cells[IX(1, size - 1, z, size)]; count++; } sum += cells[IX(0, 1, z, size)]; count++; if (!wrapY) { sum += cells[IX(0, size - 2, z, size)]; count++; } cells[IX(0, size - 1, z, size)] = sum / count; // 4 sum = 0; count = 0; sum += cells[IX(1, size - 1, z, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, size - 1, z, size)]; count++; } sum += cells[IX(size - 1, 1, z, size)]; count++; if (!wrapY) { sum += cells[IX(size - 1, size - 2, z, size)]; count++; } cells[IX(size - 1, size - 1, z, size)] = sum / count; } #endregion #region Corners // 1 sum = 0; count = 0; sum += cells[IX(size - 2, 0, 0, size)]; count++; if (!wrapX) { sum += cells[IX(1, 0, 0, size)]; count++; } sum += cells[IX(0, size - 2, 0, size)]; count++; if (!wrapY) { sum += cells[IX(0, 1, 0, size)]; count++; } sum += cells[IX(0, 0, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(0, 0, 1, size)]; count++; } cells[IX(0, 0, 0, size)] = sum / count; // 2 sum = 0; count = 0; sum += cells[IX(size - 2, size - 1, 0, size)]; count++; if (!wrapX) { sum += cells[IX(1, size - 1, 0, size)]; count++; } sum += cells[IX(0, 1, 0, size)]; count++; if (!wrapY) { sum += cells[IX(0, size - 2, 0, size)]; count++; } sum += cells[IX(0, size - 1, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(0, size - 1, 1, size)]; count++; } cells[IX(0, size - 1, 0, size)] = sum / count; // 3 sum = 0; count = 0; sum += cells[IX(size - 2, 0, size - 1, size)]; count++; if (!wrapX) { sum += cells[IX(1, 0, size - 1, size)]; count++; } sum += cells[IX(0, size - 2, size - 1, size)]; count++; if (!wrapY) { sum += cells[IX(0, 1, size - 1, size)]; count++; } sum += cells[IX(0, 0, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(0, 0, size - 2, size)]; count++; } cells[IX(0, 0, size - 1, size)] = sum / count; // 4 sum = 0; count = 0; sum += cells[IX(size - 2, size - 1, size - 1, size)]; count++; if (!wrapX) { sum += cells[IX(1, size - 1, size - 1, size)]; count++; } sum += cells[IX(0, 1, size - 1, size)]; count++; if (!wrapY) { sum += cells[IX(0, size - 2, size - 1, size)]; count++; } sum += cells[IX(0, size - 1, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(0, size - 1, size - 2, size)]; count++; } cells[IX(0, size - 1, size - 1, size)] = sum / count; // 5 sum = 0; count = 0; sum += cells[IX(1, 0, 0, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, 0, 0, size)]; count++; } sum += cells[IX(size - 1, size - 2, 0, size)]; count++; if (!wrapY) { sum += cells[IX(size - 1, 1, 0, size)]; count++; } sum += cells[IX(size - 1, 0, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(size - 1, 0, 1, size)]; count++; } cells[IX(size - 1, 0, 0, size)] = sum / count; // 6 sum = 0; count = 0; sum += cells[IX(1, size - 1, 0, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, size - 1, 0, size)]; count++; } sum += cells[IX(size - 1, 1, 0, size)]; count++; if (!wrapY) { sum += cells[IX(size - 1, size - 2, 0, size)]; count++; } sum += cells[IX(size - 1, size - 1, size - 2, size)]; count++; if (!wrapZ) { sum += cells[IX(size - 1, size - 1, 1, size)]; count++; } cells[IX(size - 1, size - 1, 0, size)] = sum / count; // 7 sum = 0; count = 0; sum += cells[IX(1, 0, size - 1, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, 0, size - 1, size)]; count++; } sum += cells[IX(size - 1, size - 2, size - 1, size)]; count++; if (!wrapY) { sum += cells[IX(size - 1, 1, size - 1, size)]; count++; } sum += cells[IX(size - 1, 0, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(size - 1, 0, size - 2, size)]; count++; } cells[IX(size - 1, 0, size - 1, size)] = sum / count; // 8 sum = 0; count = 0; sum += cells[IX(1, size - 1, size - 1, size)]; count++; if (!wrapX) { sum += cells[IX(size - 2, size - 1, size - 1, size)]; count++; } sum += cells[IX(size - 1, 1, size - 1, size)]; count++; if (!wrapY) { sum += cells[IX(size - 1, size - 2, size - 1, size)]; count++; } sum += cells[IX(size - 1, size - 1, 1, size)]; count++; if (!wrapZ) { sum += cells[IX(size - 1, size - 1, size - 2, size)]; count++; } cells[IX(size - 1, size - 1, size - 1, size)] = sum / count; #endregion }
private static void SetBoundry_BlockedCells(SetBoundsType whichSide, double[] cells, BoundrySettings boundrySettings) { //NOTE: It is safe to ignore blocked cells that are interior (surrounded by other blocked cells) IndexLERP[] blocked = null; switch (whichSide) { case SetBoundsType.VelocityX: blocked = boundrySettings.Blocked_VelocityX; break; case SetBoundsType.VelocityY: blocked = boundrySettings.Blocked_VelocityY; break; case SetBoundsType.VelocityZ: blocked = boundrySettings.Blocked_VelocityZ; break; case SetBoundsType.Ink: case SetBoundsType.Other: blocked = boundrySettings.Blocked_Other; break; default: throw new ApplicationException("Unknown SetBoundsType: " + whichSide.ToString()); } foreach (IndexLERP index in blocked) { // Add up the neighbors double newValue = 0d; if (index.Neighbors != null) { foreach (var neighbor in index.Neighbors) { newValue += cells[neighbor.Item1] * neighbor.Item2; } } // Store the average cells[index.Index1D] = newValue; } }
private static void SetBoundry_OpenSlaved(SetBoundsType whichSide, double[] cells, int size, BoundrySettings boundrySettings) { double[] source; switch (whichSide) { case SetBoundsType.VelocityX: source = boundrySettings.OpenBorderVelocityX; break; case SetBoundsType.VelocityY: source = boundrySettings.OpenBorderVelocityY; break; case SetBoundsType.VelocityZ: source = boundrySettings.OpenBorderVelocityZ; break; default: // Non velocity can use the standard open method SetBoundry_Open(whichSide, cells, size, boundrySettings); return; } // When slaved, this field's velocity is just a copy of the source's velocity foreach (var index in boundrySettings.OpenBorderCells) { cells[index.Offset1D] = source[index.Offset1D]; } }
private static void SetBoundry_OpenShared(SetBoundsType whichSide, double[] cells, int size, BoundrySettings boundrySettings) { // This method is sort of a combination of SetBoundry_Open and SetBoundry_OpenSlaved. It stores the average of what // open would have stored with what slave would have stored: (open + slave) / 2 double[] source; switch (whichSide) { case SetBoundsType.VelocityX: source = boundrySettings.OpenBorderVelocityX; break; case SetBoundsType.VelocityY: source = boundrySettings.OpenBorderVelocityY; break; case SetBoundsType.VelocityZ: source = boundrySettings.OpenBorderVelocityZ; break; default: // Non velocity can use the standard open method SetBoundry_Open(whichSide, cells, size, boundrySettings); return; } #region X wall int index; for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { index = IX(0, y, z, size); cells[index] = .5d * (source[index] + cells[IX(1, y, z, size)]); index = IX(size - 1, y, z, size); cells[index] = .5d * (source[index] + cells[IX(size - 2, y, z, size)]); } } #endregion #region Y wall for (int z = 1; z < size - 1; z++) { for (int x = 1; x < size - 1; x++) { index = IX(x, 0, z, size); cells[index] = .5d * (source[index] + cells[IX(x, 1, z, size)]); index = IX(x, size - 1, z, size); cells[index] = .5d * (source[index] + cells[IX(x, size - 2, z, size)]); } } #endregion #region Z wall for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { index = IX(x, y, 0, size); cells[index] = .5d * (source[index] + cells[IX(x, y, 1, size)]); index = IX(x, y, size - 1, size); cells[index] = .5d * (source[index] + cells[IX(x, y, size - 2, size)]); } } #endregion #region Edges double thisValue; for (int x = 1; x < size - 1; x++) { index = IX(x, 0, 0, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, 1, 0, size)], false) : cells[IX(x, 1, 0, size)]) + // restrict y travel when it's the y veloctiy array (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, 0, 1, size)], false) : cells[IX(x, 0, 1, size)]) // restrict z travel when it's the z velocity array ); cells[index] = .5d * (source[index] + thisValue); // now take the average of this field's value with the source. NOTE: Giving the two equal weight, that's why I don't just add the 3 and divide by 3. index = IX(x, size - 1, 0, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, size - 2, 0, size)], true) : cells[IX(x, size - 2, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, size - 1, 1, size)], false) : cells[IX(x, size - 1, 1, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(x, 0, size - 1, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, 1, size - 1, size)], false) : cells[IX(x, 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, 0, size - 2, size)], true) : cells[IX(x, 0, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(x, size - 1, size - 1, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, size - 2, size - 1, size)], true) : cells[IX(x, size - 2, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, size - 1, size - 2, size)], true) : cells[IX(x, size - 1, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); } for (int y = 1; y < size - 1; y++) { index = IX(0, y, 0, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, y, 0, size)], false) : cells[IX(1, y, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, y, 1, size)], false) : cells[IX(0, y, 1, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, y, 0, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, y, 0, size)], true) : cells[IX(size - 2, y, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, y, 1, size)], false) : cells[IX(size - 1, y, 1, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(0, y, size - 1, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, y, size - 1, size)], false) : cells[IX(1, y, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, y, size - 2, size)], true) : cells[IX(0, y, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, y, size - 1, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, y, size - 1, size)], true) : cells[IX(size - 2, y, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, y, size - 2, size)], true) : cells[IX(size - 1, y, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); } for (int z = 0; z < size - 1; z++) { index = IX(0, 0, z, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, 0, z, size)], false) : cells[IX(1, 0, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, 1, z, size)], false) : cells[IX(0, 1, z, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, 0, z, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, 0, z, size)], true) : cells[IX(size - 2, 0, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 1, z, size)], false) : cells[IX(size - 1, 1, z, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(0, size - 1, z, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, size - 1, z, size)], false) : cells[IX(1, size - 1, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, size - 2, z, size)], true) : cells[IX(0, size - 2, z, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, size - 1, z, size); thisValue = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, size - 1, z, size)], true) : cells[IX(size - 2, size - 1, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 2, z, size)], true) : cells[IX(size - 1, size - 2, z, size)]) ); cells[index] = .5d * (source[index] + thisValue); } #endregion #region Corners // Corners take average of neighbors index = IX(0, 0, 0, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, 0, 0, size)], false) : cells[IX(1, 0, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, 1, 0, size)], false) : cells[IX(0, 1, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, 0, 1, size)], false) : cells[IX(0, 0, 1, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(0, size - 1, 0, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, size - 1, 0, size)], false) : cells[IX(1, size - 1, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, size - 2, 0, size)], true) : cells[IX(0, size - 2, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, size - 1, 1, size)], false) : cells[IX(0, size - 1, 1, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(0, 0, size - 1, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, 0, size - 1, size)], false) : cells[IX(1, 0, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, 1, size - 1, size)], false) : cells[IX(0, 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, 0, size - 2, size)], true) : cells[IX(0, 0, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(0, size - 1, size - 1, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, size - 1, size - 1, size)], false) : cells[IX(1, size - 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, size - 2, size - 1, size)], true) : cells[IX(0, size - 2, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, size - 1, size - 2, size)], true) : cells[IX(0, size - 1, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, 0, 0, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, 0, 0, size)], true) : cells[IX(size - 2, 0, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 1, 0, size)], false) : cells[IX(size - 1, 1, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 0, 1, size)], false) : cells[IX(size - 1, 0, 1, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, size - 1, 0, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, size - 1, 0, size)], true) : cells[IX(size - 2, size - 1, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 2, 0, size)], true) : cells[IX(size - 1, size - 2, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 1, 1, size)], false) : cells[IX(size - 1, size - 1, 1, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, 0, size - 1, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, 0, size - 1, size)], true) : cells[IX(size - 2, 0, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 1, size - 1, size)], false) : cells[IX(size - 1, 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 0, size - 2, size)], true) : cells[IX(size - 1, 0, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); index = IX(size - 1, size - 1, size - 1, size); thisValue = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, size - 1, size - 1, size)], true) : cells[IX(size - 2, size - 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 2, size - 1, size)], true) : cells[IX(size - 1, size - 2, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 1, size - 2, size)], true) : cells[IX(size - 1, size - 1, size - 2, size)]) ); cells[index] = .5d * (source[index] + thisValue); #endregion }
private static void SetBoundry_Open(SetBoundsType whichSide, double[] cells, int size, BoundrySettings boundrySettings) { // OpenBox needs to allow open flow for the velocity, but just copy cells for everything else //NOTE: Allow outflow, but not full inflow (the inflow becomes self reinforcing, and the whole field becomes a wall of wind) //NOTE: Inflow is only an issue along the wall where the corresponding velocity array is parallel (so the velocityX array only needs to worry about the x wall. The y and z walls are safe to copy values, because the flow is perpendicular to them) #region X wall if (whichSide == SetBoundsType.VelocityX) { for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { cells[IX(0, y, z, size)] = SetBoundry_OpenSprtCap(cells[IX(1, y, z, size)], false); cells[IX(size - 1, y, z, size)] = SetBoundry_OpenSprtCap(cells[IX(size - 2, y, z, size)], true); } } } else { for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { cells[IX(0, y, z, size)] = cells[IX(1, y, z, size)]; cells[IX(size - 1, y, z, size)] = cells[IX(size - 2, y, z, size)]; } } } #endregion #region Y wall if (whichSide == SetBoundsType.VelocityY) { for (int z = 1; z < size - 1; z++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, 0, z, size)] = SetBoundry_OpenSprtCap(cells[IX(x, 1, z, size)], false); cells[IX(x, size - 1, z, size)] = SetBoundry_OpenSprtCap(cells[IX(x, size - 2, z, size)], true); } } } else { for (int z = 1; z < size - 1; z++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, 0, z, size)] = cells[IX(x, 1, z, size)]; cells[IX(x, size - 1, z, size)] = cells[IX(x, size - 2, z, size)]; } } } #endregion #region Z wall if (whichSide == SetBoundsType.VelocityZ) { for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, y, 0, size)] = SetBoundry_OpenSprtCap(cells[IX(x, y, 1, size)], false); cells[IX(x, y, size - 1, size)] = SetBoundry_OpenSprtCap(cells[IX(x, y, size - 2, size)], true); } } } else { for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, y, 0, size)] = cells[IX(x, y, 1, size)]; cells[IX(x, y, size - 1, size)] = cells[IX(x, y, size - 2, size)]; } } } #endregion #region Edges for (int x = 1; x < size - 1; x++) { cells[IX(x, 0, 0, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, 1, 0, size)], false) : cells[IX(x, 1, 0, size)]) + // restrict y travel when it's the y veloctiy array (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, 0, 1, size)], false) : cells[IX(x, 0, 1, size)]) // restrict z travel when it's the z velocity array ); cells[IX(x, size - 1, 0, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, size - 2, 0, size)], true) : cells[IX(x, size - 2, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, size - 1, 1, size)], false) : cells[IX(x, size - 1, 1, size)]) ); cells[IX(x, 0, size - 1, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, 1, size - 1, size)], false) : cells[IX(x, 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, 0, size - 2, size)], true) : cells[IX(x, 0, size - 2, size)]) ); cells[IX(x, size - 1, size - 1, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(x, size - 2, size - 1, size)], true) : cells[IX(x, size - 2, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(x, size - 1, size - 2, size)], true) : cells[IX(x, size - 1, size - 2, size)]) ); } for (int y = 1; y < size - 1; y++) { cells[IX(0, y, 0, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, y, 0, size)], false) : cells[IX(1, y, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, y, 1, size)], false) : cells[IX(0, y, 1, size)]) ); cells[IX(size - 1, y, 0, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, y, 0, size)], true) : cells[IX(size - 2, y, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, y, 1, size)], false) : cells[IX(size - 1, y, 1, size)]) ); cells[IX(0, y, size - 1, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, y, size - 1, size)], false) : cells[IX(1, y, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, y, size - 2, size)], true) : cells[IX(0, y, size - 2, size)]) ); cells[IX(size - 1, y, size - 1, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, y, size - 1, size)], true) : cells[IX(size - 2, y, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, y, size - 2, size)], true) : cells[IX(size - 1, y, size - 2, size)]) ); } for (int z = 0; z < size - 1; z++) { cells[IX(0, 0, z, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, 0, z, size)], false) : cells[IX(1, 0, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, 1, z, size)], false) : cells[IX(0, 1, z, size)]) ); cells[IX(size - 1, 0, z, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, 0, z, size)], true) : cells[IX(size - 2, 0, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 1, z, size)], false) : cells[IX(size - 1, 1, z, size)]) ); cells[IX(0, size - 1, z, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, size - 1, z, size)], false) : cells[IX(1, size - 1, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, size - 2, z, size)], true) : cells[IX(0, size - 2, z, size)]) ); cells[IX(size - 1, size - 1, z, size)] = 0.5 * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, size - 1, z, size)], true) : cells[IX(size - 2, size - 1, z, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 2, z, size)], true) : cells[IX(size - 1, size - 2, z, size)]) ); } #region FLAWED //double average1, average2, average3, average4; //for (int x = 1; x < size - 1; x++) //{ // average1 = 0.5 * (cells[IX(x, 1, 0, size)] + cells[IX(x, 0, 1, size)]); // average2 = 0.5 * (cells[IX(x, size - 2, size - 1, size)] + cells[IX(x, size - 1, size - 2, size)]); // average3 = 0.5 * (cells[IX(x, size - 2, 0, size)] + cells[IX(x, size - 1, 1, size)]); // average4 = 0.5 * (cells[IX(x, 1, size - 1, size)] + cells[IX(x, 0, size - 2, size)]); // if (whichSide == SetBoundsType.VelocityY || whichSide == SetBoundsType.VelocityZ) // { // average1 = SetBoundry_OpenBoxSprtCap(average1, false); // average2 = SetBoundry_OpenBoxSprtCap(average2, true); // average3 *= OPENINFLOWMULT; // average4 *= OPENINFLOWMULT; // } // cells[IX(x, 0, 0, size)] = average1; // cells[IX(x, size - 1, size - 1, size)] = average2; // cells[IX(x, size - 1, 0, size)] = average3; // cells[IX(x, 0, size - 1, size)] = average4; //} //for (int y = 1; y < size - 1; y++) //{ // average1 = 0.5 * (cells[IX(1, y, 0, size)] + cells[IX(0, y, 1, size)]); // average2 = 0.5 * (cells[IX(size - 2, y, size - 1, size)] + cells[IX(size - 1, y, size - 2, size)]); // average3 = 0.5 * (cells[IX(size - 2, y, 0, size)] + cells[IX(size - 1, y, 1, size)]); // average4 = 0.5 * (cells[IX(1, y, size - 1, size)] + cells[IX(0, y, size - 2, size)]); // if (whichSide == SetBoundsType.VelocityX || whichSide == SetBoundsType.VelocityZ) // { // average1 = SetBoundry_OpenBoxSprtCap(average1, false); // average2 = SetBoundry_OpenBoxSprtCap(average2, true); // average3 *= OPENINFLOWMULT; // average4 *= OPENINFLOWMULT; // } // cells[IX(0, y, 0, size)] = average1; // cells[IX(size - 1, y, size - 1, size)] = average2; // cells[IX(size - 1, y, 0, size)] = average3; // cells[IX(0, y, size - 1, size)] = average4; //} //for (int z = 0; z < size - 1; z++) //{ // average1 = 0.5 * (cells[IX(1, 0, z, size)] + cells[IX(0, 1, z, size)]); // average2 = 0.5 * (cells[IX(size - 2, size - 1, z, size)] + cells[IX(size - 1, size - 2, z, size)]); // average3 = 0.5 * (cells[IX(size - 2, 0, z, size)] + cells[IX(size - 1, 1, z, size)]); // average4 = 0.5 * (cells[IX(1, size - 1, z, size)] + cells[IX(0, size - 2, z, size)]); // if (whichSide == SetBoundsType.VelocityX || whichSide == SetBoundsType.VelocityY) // { // average1 = SetBoundry_OpenBoxSprtCap(average1, false); // average2 = SetBoundry_OpenBoxSprtCap(average2, true); // average3 *= OPENINFLOWMULT; // average4 *= OPENINFLOWMULT; // } // cells[IX(0, 0, z, size)] = average1; // cells[IX(size - 1, size - 1, z, size)] = average2; // cells[IX(size - 1, 0, z, size)] = average3; // cells[IX(0, size - 1, z, size)] = average4; //} #endregion #endregion #region Corners // Corners take average of neighbors cells[IX(0, 0, 0, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, 0, 0, size)], false) : cells[IX(1, 0, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, 1, 0, size)], false) : cells[IX(0, 1, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, 0, 1, size)], false) : cells[IX(0, 0, 1, size)]) ); cells[IX(0, size - 1, 0, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, size - 1, 0, size)], false) : cells[IX(1, size - 1, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, size - 2, 0, size)], true) : cells[IX(0, size - 2, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, size - 1, 1, size)], false) : cells[IX(0, size - 1, 1, size)]) ); cells[IX(0, 0, size - 1, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, 0, size - 1, size)], false) : cells[IX(1, 0, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, 1, size - 1, size)], false) : cells[IX(0, 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, 0, size - 2, size)], true) : cells[IX(0, 0, size - 2, size)]) ); cells[IX(0, size - 1, size - 1, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(1, size - 1, size - 1, size)], false) : cells[IX(1, size - 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(0, size - 2, size - 1, size)], true) : cells[IX(0, size - 2, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(0, size - 1, size - 2, size)], true) : cells[IX(0, size - 1, size - 2, size)]) ); cells[IX(size - 1, 0, 0, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, 0, 0, size)], true) : cells[IX(size - 2, 0, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 1, 0, size)], false) : cells[IX(size - 1, 1, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 0, 1, size)], false) : cells[IX(size - 1, 0, 1, size)]) ); cells[IX(size - 1, size - 1, 0, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, size - 1, 0, size)], true) : cells[IX(size - 2, size - 1, 0, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 2, 0, size)], true) : cells[IX(size - 1, size - 2, 0, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 1, 1, size)], false) : cells[IX(size - 1, size - 1, 1, size)]) ); cells[IX(size - 1, 0, size - 1, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, 0, size - 1, size)], true) : cells[IX(size - 2, 0, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 1, size - 1, size)], false) : cells[IX(size - 1, 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, 0, size - 2, size)], true) : cells[IX(size - 1, 0, size - 2, size)]) ); cells[IX(size - 1, size - 1, size - 1, size)] = ONETHIRD * ( (whichSide == SetBoundsType.VelocityX ? SetBoundry_OpenSprtCap(cells[IX(size - 2, size - 1, size - 1, size)], true) : cells[IX(size - 2, size - 1, size - 1, size)]) + (whichSide == SetBoundsType.VelocityY ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 2, size - 1, size)], true) : cells[IX(size - 1, size - 2, size - 1, size)]) + (whichSide == SetBoundsType.VelocityZ ? SetBoundry_OpenSprtCap(cells[IX(size - 1, size - 1, size - 2, size)], true) : cells[IX(size - 1, size - 1, size - 2, size)]) ); #region FLAWED //double average; //bool isVelocity = whichSide == SetBoundsType.VelocityX || whichSide == SetBoundsType.VelocityY || whichSide == SetBoundsType.VelocityZ; //// 1 //average = ONETHIRD * ( // cells[IX(1, 0, 0, size)] + // cells[IX(0, 1, 0, size)] + // cells[IX(0, 0, 1, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(0, 0, 0, size)] = average; //// 2 //average = ONETHIRD * ( // cells[IX(1, size - 1, 0, size)] + // cells[IX(0, size - 2, 0, size)] + // cells[IX(0, size - 1, 1, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(0, size - 1, 0, size)] = average; //// 3 //average = ONETHIRD * ( // cells[IX(1, 0, size - 1, size)] + // cells[IX(0, 1, size - 1, size)] + // cells[IX(0, 0, size - 2, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(0, 0, size - 1, size)] = average; //// 4 //average = ONETHIRD * ( // cells[IX(1, size - 1, size - 1, size)] + // cells[IX(0, size - 2, size - 1, size)] + // cells[IX(0, size - 1, size - 2, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(0, size - 1, size - 1, size)] = average; //// 5 //average = ONETHIRD * ( // cells[IX(size - 2, 0, 0, size)] + // cells[IX(size - 1, 1, 0, size)] + // cells[IX(size - 1, 0, 1, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(size - 1, 0, 0, size)] = average; //// 6 //average = ONETHIRD * ( // cells[IX(size - 2, size - 1, 0, size)] + // cells[IX(size - 1, size - 2, 0, size)] + // cells[IX(size - 1, size - 1, 1, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(size - 1, size - 1, 0, size)] = average; //// 7 //average = ONETHIRD * ( // cells[IX(size - 2, 0, size - 1, size)] + // cells[IX(size - 1, 1, size - 1, size)] + // cells[IX(size - 1, 0, size - 2, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(size - 1, 0, size - 1, size)] = average; //// 8 //average = ONETHIRD * ( // cells[IX(size - 2, size - 1, size - 1, size)] + // cells[IX(size - 1, size - 2, size - 1, size)] + // cells[IX(size - 1, size - 1, size - 2, size)]); //if (isVelocity) average *= OPENINFLOWMULT; //cells[IX(size - 1, size - 1, size - 1, size)] = average; #endregion #endregion }
private static void SetBoundry_Closed(SetBoundsType whichSide, double[] cells, int size, BoundrySettings boundrySettings) { #region X wall // When it's the x velocity, it needs to reflect off of this wall. All other cases slide along it (or in the case of non // velocities, just a copy) double reflectMult = whichSide == SetBoundsType.VelocityX ? -boundrySettings.WallReflectivity : 1d; for (int z = 1; z < size - 1; z++) { for (int y = 1; y < size - 1; y++) { cells[IX(0, y, z, size)] = reflectMult * cells[IX(1, y, z, size)]; cells[IX(size - 1, y, z, size)] = reflectMult * cells[IX(size - 2, y, z, size)]; } } #endregion #region Y wall reflectMult = whichSide == SetBoundsType.VelocityY ? -boundrySettings.WallReflectivity : 1d; for (int z = 1; z < size - 1; z++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, 0, z, size)] = reflectMult * cells[IX(x, 1, z, size)]; cells[IX(x, size - 1, z, size)] = reflectMult * cells[IX(x, size - 2, z, size)]; } } #endregion #region Z wall reflectMult = whichSide == SetBoundsType.VelocityZ ? -boundrySettings.WallReflectivity : 1d; for (int y = 1; y < size - 1; y++) { for (int x = 1; x < size - 1; x++) { cells[IX(x, y, 0, size)] = reflectMult * cells[IX(x, y, 1, size)]; cells[IX(x, y, size - 1, size)] = reflectMult * cells[IX(x, y, size - 2, size)]; } } #endregion #region Edges for (int x = 1; x < size - 1; x++) { cells[IX(x, 0, 0, size)] = 0.5 * (cells[IX(x, 1, 0, size)] + cells[IX(x, 0, 1, size)]); cells[IX(x, size - 1, 0, size)] = 0.5 * (cells[IX(x, size - 2, 0, size)] + cells[IX(x, size - 1, 1, size)]); cells[IX(x, 0, size - 1, size)] = 0.5 * (cells[IX(x, 1, size - 1, size)] + cells[IX(x, 0, size - 2, size)]); cells[IX(x, size - 1, size - 1, size)] = 0.5 * (cells[IX(x, size - 2, size - 1, size)] + cells[IX(x, size - 1, size - 2, size)]); } for (int y = 1; y < size - 1; y++) { cells[IX(0, y, 0, size)] = 0.5 * (cells[IX(1, y, 0, size)] + cells[IX(0, y, 1, size)]); cells[IX(size - 1, y, 0, size)] = 0.5 * (cells[IX(size - 2, y, 0, size)] + cells[IX(size - 1, y, 1, size)]); cells[IX(0, y, size - 1, size)] = 0.5 * (cells[IX(1, y, size - 1, size)] + cells[IX(0, y, size - 2, size)]); cells[IX(size - 1, y, size - 1, size)] = 0.5 * (cells[IX(size - 2, y, size - 1, size)] + cells[IX(size - 1, y, size - 2, size)]); } for (int z = 0; z < size - 1; z++) { cells[IX(0, 0, z, size)] = 0.5 * (cells[IX(1, 0, z, size)] + cells[IX(0, 1, z, size)]); cells[IX(size - 1, 0, z, size)] = 0.5 * (cells[IX(size - 2, 0, z, size)] + cells[IX(size - 1, 1, z, size)]); cells[IX(0, size - 1, z, size)] = 0.5 * (cells[IX(1, size - 1, z, size)] + cells[IX(0, size - 2, z, size)]); cells[IX(size - 1, size - 1, z, size)] = 0.5 * (cells[IX(size - 2, size - 1, z, size)] + cells[IX(size - 1, size - 2, z, size)]); } #endregion #region Corners // Corners take average of neighbors cells[IX(0, 0, 0, size)] = ONETHIRD * ( cells[IX(1, 0, 0, size)] + cells[IX(0, 1, 0, size)] + cells[IX(0, 0, 1, size)]); cells[IX(0, size - 1, 0, size)] = ONETHIRD * ( cells[IX(1, size - 1, 0, size)] + cells[IX(0, size - 2, 0, size)] + cells[IX(0, size - 1, 1, size)]); cells[IX(0, 0, size - 1, size)] = ONETHIRD * ( cells[IX(1, 0, size - 1, size)] + cells[IX(0, 1, size - 1, size)] + cells[IX(0, 0, size - 2, size)]); cells[IX(0, size - 1, size - 1, size)] = ONETHIRD * ( cells[IX(1, size - 1, size - 1, size)] + cells[IX(0, size - 2, size - 1, size)] + cells[IX(0, size - 1, size - 2, size)]); cells[IX(size - 1, 0, 0, size)] = ONETHIRD * ( cells[IX(size - 2, 0, 0, size)] + cells[IX(size - 1, 1, 0, size)] + cells[IX(size - 1, 0, 1, size)]); cells[IX(size - 1, size - 1, 0, size)] = ONETHIRD * ( cells[IX(size - 2, size - 1, 0, size)] + cells[IX(size - 1, size - 2, 0, size)] + cells[IX(size - 1, size - 1, 1, size)]); cells[IX(size - 1, 0, size - 1, size)] = ONETHIRD * ( cells[IX(size - 2, 0, size - 1, size)] + cells[IX(size - 1, 1, size - 1, size)] + cells[IX(size - 1, 0, size - 2, size)]); cells[IX(size - 1, size - 1, size - 1, size)] = ONETHIRD * ( cells[IX(size - 2, size - 1, size - 1, size)] + cells[IX(size - 1, size - 2, size - 1, size)] + cells[IX(size - 1, size - 1, size - 2, size)]); #endregion }
private static void SetBoundry(SetBoundsType whichSide, double[] cells, bool[] blocked, int size, BoundrySettings boundrySettings) { // Outer Edges switch (boundrySettings.BoundryType) { case FluidFieldBoundryType3D.Closed: SetBoundry_Closed(whichSide, cells, size, boundrySettings); break; case FluidFieldBoundryType3D.Open: SetBoundry_Open(whichSide, cells, size, boundrySettings); break; case FluidFieldBoundryType3D.Open_Shared: SetBoundry_OpenShared(whichSide, cells, size, boundrySettings); break; case FluidFieldBoundryType3D.Open_Slaved: SetBoundry_OpenSlaved(whichSide, cells, size, boundrySettings); break; case FluidFieldBoundryType3D.WrapAround: SetBoundry_ReachAround(whichSide, cells, size, boundrySettings); break; default: throw new ApplicationException("Unknown FluidFieldBoundryType3D: " + boundrySettings.BoundryType.ToString()); } // Blocked Cells if (boundrySettings.HasBlockedCells) { // This has similar logic to closed box, but applied facing outward around the blocked cells SetBoundry_BlockedCells(whichSide, cells, boundrySettings); } }