Example #1
0
    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;
                }
            }
        }
    }
    /*
     *  Precompute the total number of layers this nested grid will contain
     *  src - UniformGrid upon which this NestedGrid is based.
     */
    int PrecomputeNumLayers(UniformGrid <ItemT> src)
    {
        int numLayers = 1;    // Tally src layer.

        uint[] numPoints = new uint[3];
        numPoints[0] = src.GetNumPoints(0);
        numPoints[1] = src.GetNumPoints(1);
        numPoints[2] = src.GetNumPoints(2);
        uint size = numPoints[0] * numPoints[1] * numPoints[2];

        while (size > 8 /* a cell has 8 corners */)
        {   // Layer has more than 1 cell.
            ++numLayers;
            // Decimate number of cells (where #cells = #points-1):
            numPoints[0] = (uint)Mathf.Max((float)(numPoints[0] - 1) / 2, 1) + 1;
            numPoints[1] = (uint)Mathf.Max((float)(numPoints[1] - 1) / 2, 1) + 1;
            numPoints[2] = (uint)Mathf.Max((float)(numPoints[2] - 1) / 2, 1) + 1;
            size         = numPoints[0] * numPoints[1] * numPoints[2];
        }
        return(numLayers);
    }
Example #3
0
    /*
     *  Compute velocity due to vortons, for every point in a uniform grid
     *  This routine assumes CreateInfluenceTree has already executed.
     */
    void ComputeVelocityGrid()
    {
        mVelGrid.Clear();                                // Clear any stale velocity information
        mVelGrid.CopyShape(mInfluenceTree[0]);           // Use same shape as base vorticity grid. (Note: could differ if you want.)
        mVelGrid.Init();                                 // Reserve memory for velocity grid.

        uint numZ = mVelGrid.GetNumPoints(2);

        if (mUseMultithreads)
        {
            // Estimate grain size based on size of problem and number of processors.
            int grainSize = (int)Mathf.Max(1, numZ / numberOfProcessors);

            List <ManualResetEvent> handles = new List <ManualResetEvent>();
            for (var i = 0; i < numberOfProcessors; i++)
            {
                ManualResetEvent handle = new ManualResetEvent(false);
                handles.Add(handle);

                // Send the custom object to the threaded method.
                ThreadInfo threadInfo = new ThreadInfo();
                threadInfo.begin     = i * grainSize;
                threadInfo.end       = (i + 1) * grainSize;
                threadInfo.timeStep  = 0;
                threadInfo.vortonSim = this;
                threadInfo.handle    = handle;

                WaitCallback callBack = new WaitCallback(ComputeVelocityGridSliceThreaded);
                Nyahoon.ThreadPool.QueueUserWorkItem(callBack, threadInfo);
            }

            WaitHandle.WaitAll(handles.ToArray());
        }
        else
        {
            ComputeVelocityGridSlice(0, numZ);
        }
    }
    public static void ComputeJacobian(ref UniformGrid <Matrix3x3> jacobian, UniformGrid <Vector> vec)
    {
        Vector3 spacing = vec.GetCellSpacing();
        // Avoid divide-by-zero when z size is effectively 0 (for 2D domains)
        Vector3 reciprocalSpacing     = new Vector3(1.0f / spacing.x, 1.0f / spacing.y, spacing.z > float.Epsilon ? 1.0f / spacing.z : 0.0f);
        Vector3 halfReciprocalSpacing = (0.5f * reciprocalSpacing);

        uint[] dims       = { vec.GetNumPoints(0), vec.GetNumPoints(1), vec.GetNumPoints(2) };
        uint[] dimsMinus1 = { vec.GetNumPoints(0) - 1, vec.GetNumPoints(1) - 1, vec.GetNumPoints(2) - 1 };
        uint   numXY      = dims[0] * dims[1];

        uint[] index = new uint[3];


        // Compute derivatives for interior (i.e. away from boundaries).
        for (index[2] = 1; index[2] < dimsMinus1[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 1; index[1] < dimsMinus1[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 1; index[0] < dimsMinus1[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    Matrix3x3 rMatrix = jacobian[offsetX0Y0Z0];
                    /* Compute d/dx */
                    rMatrix.x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    /* Compute d/dy */
                    rMatrix.y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    /* Compute d/dz */
                    rMatrix.z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                }
            }
        }

        #region boundariesDerivatives
        // Compute derivatives for boundaries: 6 faces of box. ------------------------------------------------------------
        // In some situations, these macros compute extraneous data.
        // A tiny bit more efficiency could be squeezed from this routine,
        // but it turns out to be well under 1% of the total expense.


        // Compute derivatives for -X boundary.
        index[0] = 0;
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x;
                    }
                    else if (index[0] == dimsMinus1[0])
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    }

                    if (index[1] == 0)
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y;
                    }
                    else if (index[1] == dimsMinus1[1])
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    }

                    if (index[2] == 0)
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z;
                    }
                    else if (index[2] == dimsMinus1[2])
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                    }
                }
            }
        }

        // Compute derivatives for -Y boundary.
        index[1] = 0;
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x;
                    }
                    else if (index[0] == dimsMinus1[0])
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    }

                    if (index[1] == 0)
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y;
                    }
                    else if (index[1] == dimsMinus1[1])
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    }

                    if (index[2] == 0)
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z;
                    }
                    else if (index[2] == dimsMinus1[2])
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                    }
                }
            }
        }

        // Compute derivatives for -Z boundary.
        index[2] = 0;
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x;
                    }
                    else if (index[0] == dimsMinus1[0])
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    }

                    if (index[1] == 0)
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y;
                    }
                    else if (index[1] == dimsMinus1[1])
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    }

                    if (index[2] == 0)
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z;
                    }
                    else if (index[2] == dimsMinus1[2])
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                    }
                }
            }
        }

        // Compute derivatives for +X boundary.
        index[0] = dimsMinus1[0];
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x;
                    }
                    else if (index[0] == dimsMinus1[0])
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    }

                    if (index[1] == 0)
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y;
                    }
                    else if (index[1] == dimsMinus1[1])
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    }

                    if (index[2] == 0)
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z;
                    }
                    else if (index[2] == dimsMinus1[2])
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                    }
                }
            }
        }


        // Compute derivatives for +Y boundary.
        index[1] = dimsMinus1[1];
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x;
                    }
                    else if (index[0] == dimsMinus1[0])
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    }

                    if (index[1] == 0)
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y;
                    }
                    else if (index[1] == dimsMinus1[1])
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    }

                    if (index[2] == 0)
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z;
                    }
                    else if (index[2] == dimsMinus1[2])
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                    }
                }
            }
        }

        // Compute derivatives for +Z boundary.
        index[2] = dimsMinus1[2];
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x;
                    }
                    else if (index[0] == dimsMinus1[0])
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    }

                    if (index[1] == 0)
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y;
                    }
                    else if (index[1] == dimsMinus1[1])
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    }

                    if (index[2] == 0)
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z;
                    }
                    else if (index[2] == dimsMinus1[2])
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z;
                    }
                    else
                    {
                        jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                    }
                }
            }
            #endregion
        }
    }
Example #5
0
    /*
     *  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.
                    }
                }
            }
        }
    }
Example #6
0
    /*
     *  Compute velocity at a given point in space, due to influence of vortons
     *
     *  vPosition - point in space whose velocity to evaluate
     *  indices - indices of cell to visit in the given layer
     *  iLayer - which layer to process
     *
     *  returns velocity at vPosition, due to influence of vortons
     *  This is a recursive algorithm with time complexity O(log(N)).
     *  The outermost caller should pass in mInfluenceTree.GetDepth().
     *
     */
    Vector ComputeVelocity(Vector3 vPosition, uint[] indices, int iLayer)
    {
        Vector velocityAccumulator = new Vector();

        UniformGrid <Vorton> rChildLayer = mInfluenceTree[(uint)iLayer - 1];

        uint[] pClusterDims      = mInfluenceTree.GetDecimations(iLayer);
        uint[] clusterMinIndices = mInfluenceTree.GetChildClusterMinCornerIndex(pClusterDims, indices);

        Vector3 vGridMinCorner = rChildLayer.GetMinCorner();
        Vector3 vSpacing       = rChildLayer.GetCellSpacing();

        uint[] increment  = new uint[3];
        uint   numXchild  = rChildLayer.GetNumPoints(0);
        uint   numXYchild = numXchild * rChildLayer.GetNumPoints(1);

        // The larger this is, the more accurate (and slower) the evaluation.
        // Reasonable values lie in [0.00001,4.0].
        // Setting this to 0 leads to very bad errors, but values greater than (tiny) lead to drastic improvements.
        // Changes in margin have a quantized effect since they effectively indicate how many additional
        // cluster subdivisions to visit.
        float marginFactor = 0.0001f; // 0.4f ; // ship with this number: 0.0001f ; test with 0.4
        // When domain is 2D in XY plane, min.z==max.z so vPos.z test below would fail unless margin.z!=0.
        Vector3 margin = marginFactor * vSpacing + (0.0f == vSpacing.z ? new Vector3(0, 0, float.Epsilon) : Vector3.zero);

        // For each cell of child layer in this grid cluster...
        for (increment[2] = 0; increment[2] < pClusterDims[2]; ++increment[2])
        {
            uint[] idxChild = new uint[3];
            idxChild[2] = clusterMinIndices[2] + increment[2];
            Vector3 vCellMinCorner, vCellMaxCorner;
            vCellMinCorner.z = vGridMinCorner.z + (float)(idxChild[2]) * vSpacing.z;
            vCellMaxCorner.z = vGridMinCorner.z + (float)(idxChild[2] + 1) * vSpacing.z;
            uint offsetZ = idxChild[2] * numXYchild;
            for (increment[1] = 0; increment[1] < pClusterDims[1]; ++increment[1])
            {
                idxChild[1]      = clusterMinIndices[1] + increment[1];
                vCellMinCorner.y = vGridMinCorner.y + (float)(idxChild[1]) * vSpacing.y;
                vCellMaxCorner.y = vGridMinCorner.y + (float)(idxChild[1] + 1) * vSpacing.y;
                uint offsetYZ = idxChild[1] * numXchild + offsetZ;
                for (increment[0] = 0; increment[0] < pClusterDims[0]; ++increment[0])
                {
                    idxChild[0]      = clusterMinIndices[0] + increment[0];
                    vCellMinCorner.x = vGridMinCorner.x + (float)(idxChild[0]) * vSpacing.x;
                    vCellMaxCorner.x = vGridMinCorner.x + (float)(idxChild[0] + 1) * vSpacing.x;
                    if (
                        (iLayer > 1) &&
                        (vPosition.x >= vCellMinCorner.x - margin.x) &&
                        (vPosition.y >= vCellMinCorner.y - margin.y) &&
                        (vPosition.z >= vCellMinCorner.z - margin.z) &&
                        (vPosition.x < vCellMaxCorner.x + margin.x) &&
                        (vPosition.y < vCellMaxCorner.y + margin.y) &&
                        (vPosition.z < vCellMaxCorner.z + margin.z)
                        )
                    {   // Test position is inside childCell and currentLayer > 0...
                        // Recurse child layer.
                        Vector upVelocicy = ComputeVelocity(vPosition, idxChild, iLayer - 1);
                        velocityAccumulator += upVelocicy;
                    }
                    else
                    {   // Test position is outside childCell, or reached leaf node.
                        //    Compute velocity induced by cell at corner point x.
                        //    Accumulate influence, storing in velocityAccumulator.
                        uint offsetXYZ = idxChild[0] + offsetYZ;

                        // Add velocity due to this vorton to the accumulator
                        rChildLayer[offsetXYZ].AccumulateVelocity(ref velocityAccumulator, vPosition);
                    }
                }
            }
        }


        return(velocityAccumulator);
    }
    public static void ComputeJacobian(ref UniformGrid<Matrix3x3> jacobian, UniformGrid<Vector> vec)
    {
        Vector3 spacing = vec.GetCellSpacing();
        // Avoid divide-by-zero when z size is effectively 0 (for 2D domains)
        Vector3 reciprocalSpacing = new Vector3(1.0f / spacing.x , 1.0f / spacing.y, spacing.z > float.Epsilon ? 1.0f / spacing.z : 0.0f ) ;
        Vector3 halfReciprocalSpacing = (0.5f * reciprocalSpacing) ;

        uint[] dims = { vec.GetNumPoints(0), vec.GetNumPoints(1), vec.GetNumPoints(2) };
        uint[] dimsMinus1 = { vec.GetNumPoints(0) - 1, vec.GetNumPoints(1) - 1, vec.GetNumPoints(2) - 1 };
        uint numXY = dims[0] * dims[1];
        uint[] index = new uint[3];

        // Compute derivatives for interior (i.e. away from boundaries).
        for (index[2] = 1; index[2] < dimsMinus1[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 1; index[1] < dimsMinus1[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 1; index[0] < dimsMinus1[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    Matrix3x3 rMatrix = jacobian[offsetX0Y0Z0];
                    /* Compute d/dx */
                    rMatrix.x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x;
                    /* Compute d/dy */
                    rMatrix.y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y;
                    /* Compute d/dz */
                    rMatrix.z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z;
                }
            }
        }

        #region boundariesDerivatives
        // Compute derivatives for boundaries: 6 faces of box. ------------------------------------------------------------
        // In some situations, these macros compute extraneous data.
        // A tiny bit more efficiency could be squeezed from this routine,
        // but it turns out to be well under 1% of the total expense.

        // Compute derivatives for -X boundary.
        index[0] = 0;
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; }
                    else if (index[0] == dimsMinus1[0])
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; }
                    else
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; }

                    if (index[1] == 0)
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; }
                    else if (index[1] == dimsMinus1[1])
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; }
                    else
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; }

                    if (index[2] == 0)
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; }
                    else if (index[2] == dimsMinus1[2])
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; }
                    else
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; }

                }
            }
        }

        // Compute derivatives for -Y boundary.
        index[1] = 0;
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; }
                    else if (index[0] == dimsMinus1[0])
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; }
                    else
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; }

                    if (index[1] == 0)
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; }
                    else if (index[1] == dimsMinus1[1])
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; }
                    else
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; }

                    if (index[2] == 0)
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; }
                    else if (index[2] == dimsMinus1[2])
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; }
                    else
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; }
                }
            }
        }

        // Compute derivatives for -Z boundary.
        index[2] = 0;
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; }
                    else if (index[0] == dimsMinus1[0])
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; }
                    else
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; }

                    if (index[1] == 0)
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; }
                    else if (index[1] == dimsMinus1[1])
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; }
                    else
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; }

                    if (index[2] == 0)
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; }
                    else if (index[2] == dimsMinus1[2])
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; }
                    else
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; }
                }
            }
        }

        // Compute derivatives for +X boundary.
        index[0] = dimsMinus1[0];
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; }
                    else if (index[0] == dimsMinus1[0])
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; }
                    else
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; }

                    if (index[1] == 0)
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; }
                    else if (index[1] == dimsMinus1[1])
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; }
                    else
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; }

                    if (index[2] == 0)
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; }
                    else if (index[2] == dimsMinus1[2])
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; }
                    else
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; }
                }
            }
        }

        // Compute derivatives for +Y boundary.
        index[1] = dimsMinus1[1];
        for (index[2] = 0; index[2] < dims[2]; ++index[2])
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; }
                    else if (index[0] == dimsMinus1[0])
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; }
                    else
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; }

                    if (index[1] == 0)
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; }
                    else if (index[1] == dimsMinus1[1])
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; }
                    else
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; }

                    if (index[2] == 0)
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; }
                    else if (index[2] == dimsMinus1[2])
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; }
                    else
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; }
                }
            }
        }

        // Compute derivatives for +Z boundary.
        index[2] = dimsMinus1[2];
        {
            //ASSIGN_Z_OFFSETS
            uint offsetZM = numXY * (index[2] - 1);
            uint offsetZ0 = numXY * index[2];
            uint offsetZP = numXY * (index[2] + 1);

            for (index[1] = 0; index[1] < dims[1]; ++index[1])
            {
                //ASSIGN_YZ_OFFSETS
                uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0;
                uint offsetY0Z0 = dims[0] * index[1] + offsetZ0;
                uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0;
                uint offsetY0ZM = dims[0] * index[1] + offsetZM;
                uint offsetY0ZP = dims[0] * index[1] + offsetZP;

                for (index[0] = 0; index[0] < dims[0]; ++index[0])
                {
                    //ASSIGN_XYZ_OFFSETS
                    uint offsetX0Y0Z0 = index[0] + offsetY0Z0;
                    uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0;
                    uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0;
                    uint offsetX0YMZ0 = index[0] + offsetYMZ0;
                    uint offsetX0YPZ0 = index[0] + offsetYPZ0;
                    uint offsetX0Y0ZM = index[0] + offsetY0ZM;
                    uint offsetX0Y0ZP = index[0] + offsetY0ZP;

                    //COMPUTE_FINITE_DIFF
                    if (index[0] == 0)
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; }
                    else if (index[0] == dimsMinus1[0])
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; }
                    else
                    { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; }

                    if (index[1] == 0)
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; }
                    else if (index[1] == dimsMinus1[1])
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; }
                    else
                    { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; }

                    if (index[2] == 0)
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; }
                    else if (index[2] == dimsMinus1[2])
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; }
                    else
                    { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; }
                }
            }
            #endregion
        }
    }