Computational fluid dynamics (CFD) is an indispensable tool in aerospace engineering. These expensive simulations produce a large amount of three-dimensional flow data, and in order to gain understanding from the results we employ interactive visualization. We generally use purpose-built desktop applications such as Tecplot or Ensight to do this. However, the software is expensive and this makes it difficult to share results (other than in simple 2D renderings). In this post, I will demonstrate how to create web-based, interactive, 3D visualizations of surface pressure data using a free and open-source software stack.
Check out this example result from one of my earlier research papers! (Flexible Formulation of Spatial Integration Constraints in Aerodynamic Shape Optimization )
I’m very impressed with the plotly graphing library for Python. Previously, I’ve mostly used the matplotlib package for static visualization, and bokeh for interactive charts. In my view, plotly is now the best available free tool for creating interactive charts in Python.
The Plotly Express API makes it trivially easy to create and animate simple charts (like scatters and bars). If you need more detailed control over your figures, there is a lower-level graph object API which is what we will be using today.
To install Plotly, I recommend starting from the Anaconda Python 3 distribution.
Once you have a working Anaconda installation, from your command line, type conda install plotly
and you should be all set.
Check out the examples and tutorials available on the plotly Python reference page.
We will be using the Mesh3d
graph object today.
To familiarize yourself, check out the Mesh3d examples page.
The Mesh3d
trace takes a triangulated surface and renders it in 3D, complete with lighting effects and shading.
I’ll briefly cover the format plotly expects to see.
The Mesh3d
constructor takes several arguments, including the following geometry data:
Alternatively, you can provide a point cloud (x, y, z only) and plotly can try to construct a Delaunay triangulation of the object, and the plotly Mesh3d
example page provides an example of this. However, I don’t recommend this approach if you have CFD data since you already know the structure of the mesh.
Our lab’s workflow generates surface pressure distributions as .cgns files which we postprocess in Tecplot 360. It is highly capable software, but it’s also very expensive. We would like to be able to show interactive, volume-rendered CFD data to the general public without the need for a local Tecplot installation.
Data-Alter-Specify Equations
. In the pop-up window, input {p3D}={myvarname}
where myvarname
is the parameter you want to plot. Select New var location
to be Node
. Then click Compute
.File-Write Data
. In the pop-up window, choose a file name, keeping the .dat
file extension and click Save
. In the next dialog, select X
, Y
, Z
, and p3d
in the Variable(s)
section (in that order). Choose the Zone(s)
you want to plot (one or more is OK). Choose point
for the format (important).You should see a four-column output format like this:
If you have structured CFD data exported from Tecplot in the process described above, the following conversion process applies to you.
You can tell if your data is structured because the header of your .dat
file will read something like this:
I=7, J=7, K=1, ZONETYPE=Ordered
If you have unstructured data you can use this script as inspiration but you’ll have to write your own parser.
I wrote a Python script to convert a structured Tecplot .dat
file into the x, y, z, i, j, k, intensity format.
The main challenge is that structured surface mesh data usually consists of quadrilaterals, whereas plotly wants triangles.
Therefore we have to triangulate each quad, which is trivial (you just bisect it corner to corner).
I ensured that the triangle normal vectors are pointing in a consistent direction which helps plotly render correctly.
Note that Tecplot saves its output so that i changes fastest, then j, then k (though for surface data k should always be 1).
Now that we have CFD data in the proper format, we can plot it easily using plotly.
We use the plotly.io
API to generate output in HTML format.
The bulk of the chart data is actually stored as Javascript.
The plotly rendering engine itself is loaded as a huge Javascript file called plotly.js
.
We use the option include_plotlyjs='cdn'
, which will automatically download plotly.js
from the internet instead of bundling it with the HTML file (which greatly increases its size).
If everything went well, we should have a working visualization open in the browser! You can now redistribute this HTML file to anybody with a modern browser and they should be able to open it.
There is an easy way to embed plotly results, and an elegant way.
The easy way is to simply use an iframe
element to directly embed the plotly HTML file in your site.
However, since the plotly.js
file is so huge (3 MB), this will slow down your page load.
A better way is to load the visualization on demand! This requires a little bit of web frontend work using Javascript and CSS. We want to create a button which, once clicked, loads the huge plotly Javascript files and then creates space for the visualization.
First, open the HTML file from the previous step. You should see a line similar to this:
<div id="d58f9b77-df41-4618-b6d2-baa7fb02b0a6" class="plotly-graph-div" style="height:100%; width:100%;"></div>
The unique identifier, height, and width information will come in handy later.
Below this line you should see a big <script>
element with your plot data.
Save the interior of the <script>
element (without the actual <script>
tags) as a separate Javascript file with a .js
extension.
Make sure your site can serve it as a static file.
Next, we create a button in our web page and tag it with some CSS classes to style it.
I use Bootstrap 3 so the btn
and btn-lg
classes apply colors and some effects.
<button class="btn btn-lg">Click to load</button>
Now we need to add some HTML5 data to the button so we know some information about the visualization for this interaction. Let’s add the following attributes:
plotly.io
<script>
tags).900px
or 100%
Let’s also tag it with a new CSS class called btn-hidden-viz
.
The HTML code for our button now looks like this:
<button class="btn btn-lg btn-hidden-viz"
data-plotly-src="/assets/viz/cfdexample.js"
data-plotly-uid="d58f9b77-df41-4618-b6d2-baa7fb02b0a6"
data-height="100%"
data-width="100%">
Click to load
</button>
Next, I want to create a click handler which loads plotly.js
from a CDN, then loads my visualization, and finally inserts it into the page and disappears.
I use jQuery’s click handler to apply the interaction to all buttons with the btn-hidden-viz
class.
I use jQuery ajax
to ensure that the plotly.js
file is loaded before the visualization is opened.
I also set a 10 second timeout in case the user is on a slow connection or has lost connection to the CDN.
This snippet does the trick. You’ll want to add it to your site’s Javascript preload file.
Finally, let’s add a spinner effect while the visualization is loading.
I did this by adding a <span>
element with these classes to the button during the ajax
calls:
glyphicon glyphicon-refresh glyphicon-refresh-animate
We include the following snippet (which I swiped from a CSS showcase somewhere) in our site’s CSS to get the animation to work properly:
And there you have it! The final product looks like this:
Plotly is the best way I have found so far to render CFD results interactively on the internet for free.
You can find the code I used for this example and a sample Tecplot .dat
file here.