Subdivision applied to an initial coarse vector field

|
|
|
| It is not hard to see from this example that the subdivision scheme refines the vector field in a way that is intuitively pleasing. Given the initial vector field from the first frame, the subsequent frames define refined fields that, in the context of fluid flow, make sense from a physically intuitive point of view. Below is another example, this one being perhaps defined by an initial field whose conditions are more like those one would expect from fluid flow. | |
|
|
|

|
|
|
| It is immediately clear from the above two examples why a subdivision scheme such as this can be a great benefit to a user wishing to specify some initial conditions for simulation. With minimal effort, the user can specify a coarse vector field that represents the forces of the system in which an object is being eroded. The subdivision scheme will then interpolate the remaining data and yield a dense field that accurately represents external forces governed by the laws of fluid dynamics. Obviously, this greatly reduces the burden of the user. While this is a great advantage, it would be even more helpful if the user were able to specify additional information or place constraints on this field. For example, the user may wish to specify a dynamic field - one that varies with time, or one with boundaries that confine the field to an irregular domain. Such conditions deviate from the standard subdivision scheme for vector fields and thus require the subdivision scheme to be altered. Therefore, I explored ways to extend the standard subdivision scheme already implemented to include such conditions. | |
|
|
|
| Subdivision Over Irregular Domains | |
|---|---|
| In their derivation of the subdivision scheme for fluid flow, Weimer and Warren dealt only with refining coarse vector fields that were initially specified over domains of equal dimension - an NxN grid in the two dimensional case. It seems that it would be beneficial to extend this framework to a more general setting, one in which the domain were not restricted to an NxN grid. To achieve this, I altered the original subdivision scheme put forth by Weimer and Warren. Their scheme implicitly assumed that every point in the domain should be given a refined value after each iteration. By restricting the scheme to eliminate this assumption it is possible to refine coarse vector fields over vastly varied domains. Particularly, this can be incorporated into the subdivision scheme by resetting the values of certain sections of the domain after each iteration. For example, if one were studying the flow of fluid through an area which contained an obstruction, the scheme would consider the entire area (including the obstruction), but after each iteration the points in the area containing the obstruction would be reset to zero, as no fluid would flow through the obstruction. (Remember, the values of each vector represent the fluid velocity, so this modification ensures that the fluid velocity is always zero inside the obstruction.) The following screen shots display this type of subdivision. | |
|
|
|
|
|
|
| Dynamic Elements | |
|---|---|
|
I attempted to incorporate an element of dynamics into the subdivision scheme by allowing an object to be specified in the way described above (nothing more than a set of boundary conditions that allow no flow in a certain area - analagous to an object in the field), along with a set of directions on how the object will move over some time step. This was done in the following way: set the boundary for the object's initial position, refine the coarse field around the object just as in the irregular domain case, set the new boundary conditions for the object's position after movement, subdivide the old position of the object (the hole left after movement) to fill that spot in. While this approach did provide results that might agree somewhat with intuition, there were some problems. There seems to be some discrepency at the borders of the filled-in hole and the subdivided field. This happens because the hole is filled in without any regard to the overall conditions of the field - it doesn't know that it should have to match up with the external field.
There seem to be two fixes to this problem: Implementing the first method described above did little to alleviate the problem. As the inner domain being refined still had no knowledge of the outer boundary conditions, it failed to force the vector field to correctly match up at the edges. It seems that the only way to fix this problem would be to devise a subdivision scheme in which the inner domain being filled in were forced to match the outer boundary conditions. See below for screen shots of the problem. |
|
|
The only solution that provided results which seem to agree with one's physical intuition were obtained by resubdividing the entire domain. This, however, is undesirable because it greatly increases the time complexity of the problem at hand. |
|
|
|
| Porting to C++ | |
|---|---|
|
Weimer and Warren have already implemented their subdivision scheme in C++. The code is
freely available on their website. I modified
their code, writing a more suitable front end for our purposes and deleting much of their code that had to do with various OpenGL issues
that were already taken care of in Doug and Philip's code.
The modified code is broken up as follows: --VectorField3d.cpp is the main data structure that stores any vector field (coarse or fine) used in the subdivision scheme. The underlying stucture is a dynamically allocated flat array that stores the x,y,z components of the field's velocity at that point by looping over the x,y,z points in the field with z as the fastest index and x as the slowest. Also, the first three values in the array store the x,y,z dimensions of the field. So, for example, if the array consists of 3 3 3 1 1 1 2 2 2 ... the vector field is a 3x3x3 field with an x component of 1 at the point (0,0,0), a y component of 1 at the point (0,0,0), a z component of 1 at the point (0,0,0), an x component of 2 at the point (0,0,1) and so forth. Also included in VectorField.cpp are the associated functions such as trimField(), which returns the center 2n-1 block of values as needed. --FlowSubdiv3D.cpp contains the actual subdivision code, which is mostly just the implementation of discrete convolution. The code in FlowSubdiv3D.cpp takes the coarse vector field and the mask and appropriately combines the two, resulting in the refined vector field. --Mask3D.cpp is much like VectorField3D.cpp, except that it is used to store the mask being used. As with VectorField3D, the mask is stored in a single flat array in the same format. Also included are the associated mask functions. --subdivide.cpp is the front end I wrote to accomodate Philip's code. The arguments provided to subdivide.cpp define the number of times to run the subdivision scheme, the size of the mask to use, the coarse vector field and the space allocated for the refined vector field. To use apply the subdivision scheme, simply include subdivide.h and call subdivide() with the appropriate arguments. Open the source code and see the comments for more information on how to use it. | |