What is a Distance Field and Why Do I Care?
Before we begin, we should define the signed distance field being computed by this new function. Put simply, the distance field is a nodal, scalar quantity which is the minimum distance from each node to any node on a target part. In EnSight, the field is estimated from the individual node positions. No consideration is given to element boundaries in the computation. In this sense, it is a purely nodal computation and thus its accuracy is strongly linked to spatial nodal density.
How might one use a distance field? A classic example is the computation of “clearance” or the minimum distance between two parts that are moving relative to each other over time. In the example below, some ordinance is simulated dropping from the wing of a plane. We use the Dist2Part() function to compute a “clearance” field that is the minimum distance between each node on the dropping ordinance and the nodes on the parts that make up the plane model.
In the animation, we color all of the parts by the computed field and use a banded palette to simulate contouring. The result is a fairly compelling view of the areas of concern.
Once we have computed the clearance as a nodal scalar field using the Dist2Part() function, we can extend this over time using a minimum over time query. The result is shown here:
The plot illustrates the minimum clearance value over all of the destination parts as a function of time and reflects some of the temporal, rotational complexities of the model.
The Dist2Part() Function
Specifically, the Dist2Part() calculator function has three parameters. It takes the form:
Dist2Part(destination_parts, target_part_number, nodal_normal_vector or -1)
- The first argument (destination_parts) is a list of parts on which to compute the scalar field. The function will return a nodal scalar field on all of the selected destination parts.
- The second argument (target_part_number) is the integer number for the part to compute the distance from. Note that the target part must be included in the destination part list and the field returned on the target part has the value 0.0 at all nodes.
- The final argument (nodal_normal_vector) selects between a unsigned or signed distance field. If the scalar value -1 is used as this argument, the function will compute the unsigned distance field as described above. The other option is to specify a nodal vector field defined on the target part. This vector is used to define an “inside” and “outside” of a part which is mapped to the sign of the distance returned.
One important thing to remember. In EnSight, displacements are generally applied on the client (using the displacement icon) for visual display. The node locations on the server, where the Dist2Part() function is computed are not displaced. Thus, if one naively applies Dist2Parts() to a dataset that relies on displacements to move nodes (and thus parts) over time, the field may not vary over time as expected. EnSight does support server-side displacements and they can be used to address this issue. If one uses server-side displacements with the “Adjust part coordinates:” options in the Creation attributes of the Model part Feature Detail Editor, these will be used in the Dist2Part() computation.
Signed Distance Fields?
In many cases (e.g. clearance computations), the basic distance field is sufficient, but we can extend the computation to include the concept of “inside” and “outside” of a surface or part. This is done using a vector quantity defined at all of the nodes of the target part referred to as the nodal normal. The function still selects the shortest distance between the destination part node and all of the nodes on the target part. However, after selecting a node on the target part, EnSight computes the dot product between the user supplied normal vector and a vector from the selected target part node and the current destination node. The sign of that operation is applied to the distance. What does this look like in action? A simple example is shown below. We take the AMI model and create a tilted clipping plane. Next, we compute the normal of the part (using the Normal() function) and then convert it to a nodal quantity (using ElemToNode()). The result is displayed as a vector arrow part in the image. Finally, we compute Dist2Part() using the clip plane as the target, all of the parts as the source and the nodal normal vector.
In the image, one can note that all of the mesh nodes on the side of the clip as the normals point are positive and the values in the other direction are negative.
Note that there is no restriction on the target part normal vector at all. In most cases, users will compute a normal on a 2D surface or a 3D gradient and map this to the part nodes. However, any arbitrary vector field can be used. This allows for operations such as projections, where a single vector value might be applied to a 1D part. The zero crossing of the signed field computed using that 1D part as the target is effectively the projection of the 1D part through the other parts. Iso-volume computations could then be used to clip a part to the projection of a 1D part for example. There are a lot of other cases where the signed distance can be very useful.
A Little Benchmarking
The distance field computation as implemented in EnSight is an O(N*M) operation in the number of nodes in the target (N) and in the parts being computed on (M). One should use some caution when applying it to large numbers of nodes. The function can be used with the EnSight Server of Servers functionality to compute the computation in distributed parallel, but it is also threaded within a server as well. Let’s take a look at how well it parallelizes.
The example used here is from the “clearance” dataset used to generate the first animation. The amount of time it takes to load and render the animation was timed for 1 and 8 threads on an HP xw8400 workstation. This system was outfitted with dual 4 core 2.4Ghz Intel processors, 10GB of RAM and an nVidia Quadro FX 4600 graphics card. The system ran MS Windows XP Professional x86 (64bit Windows). The dataset itself is around 17.5GB in size with 72 time steps being used. The ENSIGHT8_MAX_THREADS environmental variable was set to 8 and 1 for the tests. The numbers are in seconds with the first number being the total (wall clock) time and the number in parenthesis being the amount of time used on the server.
|1 Thread||8 Threads||Speedup|
|Initial dataset setup||201 (171)||170 (140)||1.18 (1.22)|
|Rendering 72 frames||1637 (1589)||256 (209)||6.39 (7.60)|
From the numbers, the initial time is dominated by I/O and other operations, but once the visualization is prepared, we see large gains for using the additional threads (ideally 8x). Perhaps most important is that the animation could be loaded and rendered in its entirety in around 7 minutes with threading enabled vs over 30 minutes without!
Some Closing Thoughts
These are obviously exceptional results utilizing functions that are known to parallelize well in EnSight, so not all command files set these sorts of wins, but it does give some idea as to what can be achieved. CEI continues to improve its threaded functionality in every release, so stay tuned!