Improving Code Performance by using the InterCall Compiler

by Terry Robb
<tdr@vaxc.cc.monash.edu.au>

Producing a Fire Animation

What is it?

This animation has nothing to do with the real dynamics of fire!. It's actually just a texture animation and is based on standard noise generating techniques. By transforming the noise one can produce many types of textured patterns which, depending on the transformation, can be made to look remarkably like marble, wood, or even fire as above.

One problem with animating these types of textures is that they take an awful lot of time to compute in Mathematica. A variant of the code below, for exampe, would apparently take 10 hours to produce a 60 frame low-resolution animation. In this implementation we instead use InterCall to drastically improve the performance. The moderately high-resolution animation above took about 10 minutes to produce using the InterCall pseudo-code compiler. Virtually no re-writing of Mathematica code was necessary.

Fire Code

Here's a basic implementation of a fire texture:

fireCode = Function[{a,nx,ny,xrange,yrange,rands,k},
  Block[{t,f,xs,ys,xoffset=0.01*k,yoffset=0.10*k,
         xmin,xmax,ymin,ymax,dx,dy, i,j,
         vx0,vx1,vy0,vy1,cx,cy,fx,fy,xx,yy,
         ix0=0,iy0=0,ix1=0,iy1=0,ly0=0,ly1=0},
    {xmin, xmax} = xrange; dx = (xmax-xmin)/(nx-1);
    {ymin, ymax} = yrange; dy = (ymax-ymin)/(ny-1);
    xmin += xoffset;
    ymin += yoffset;
    a = Table[
      xs  = xmin + (i-1)*dx;
      ys  = ymin + (j-1)*dy;
      t  = 0.0;
      f  = 1.0;
      Do[
        xx = f*xs;
        yy = f*ys;
        ix0 = Floor[xx]; ix1 = ix0+1; fx = xx - ix0;
        iy0 = Floor[yy]; iy1 = iy0+1; fy = yy - iy0;
        cx = (3.0 - 2.0*fx)*fx^2;
        cy = (3.0 - 2.0*fy)*fy^2;

        ly0 = rands[[ Mod[      iy0, 101] + 1 ]];
        vx0 = rands[[ Mod[ix0 + ly0, 101] + 1 ]];
        vx1 = rands[[ Mod[ix1 + ly0, 101] + 1 ]];
        vy0 = vx0 + cx*(vx1-vx0);

        ly1 = rands[[ Mod[      iy1, 101] + 1 ]];
        vx0 = rands[[ Mod[ix0 + ly1, 101] + 1 ]];
        vx1 = rands[[ Mod[ix1 + ly1, 101] + 1 ]];
        vy1 = vx0 + cx*(vx1-vx0);

        t += Abs[(vy0+cy*(vy1-vy0))/32767.0 - 0.5]/f;
        f += f, {5}];
      Min[1., Max[0., 2.0*t - (ys-yoffset)]],
      {i, 1, nx}, {j, 1, ny}
    ];
  ], HoldFirst];

The code above sums five octaves of a noise function and then applies a simple transformation (that's the part: 2.0*t-(ys-yoffset)) to create a fire effect. For each frame in the animation the fire texture is shifted slightly upwards, which has the effect of making the flames appear to rise. To make a marble effect try using Sin[t] etc.

The noise function is based on a random number table (rands), which is addressed according to the hash code of the co-ordinates in the texture array (a). Here we create a table:

SeedRandom[555];
rands = Table[Random[Integer, 32767], {101}];

Directly running fireCode is very slow. Flip Phillips had a variant of fireCode which he said took about 10 hours to produce a very low resolution version of the animation done above. One can considerably speed up the performance by simply running fireCode through the InterCall pseudo-code compiler named InterCallCompile. By such a technique the animation can be produced in about 10 minutes, which is mostly the time involved in rendering the frames.

First we need to load the InterCall package:

<<InterCall.m;

And then we can use the following command to compile fireCode. (The last argument specifies the datatype of fireCode.)

fire = InterCallCompile[fireCode,
           S[R[#2,#3], I, I, R[2], R[2], I[101], R]];

If you are using InterCall version 2.1 or earlier, then open this cell.

For InterCall version 2.1 or earlier you need to compile fireCode as below. Later versions of InterCall let you do this with the command called InterCallCompile.

fire = Peek @ AddDefault[$FIRE->fireCode,
           S[R[#2,#3], I, I, R[2], R[2], I[101], R]];

We now proceed exactly as normal, except instead of using the fireCode function, we now use the much faster InterCall-compiled fire command. Here's the sequence of commands that was used to produce the animation:

nx = ny = 61; tmax = 60;
Do[
 fire[None,nx,ny,{-2,2},{0,1.7},rands,k]; a=Peek[1];
 ListDensityPlot[a//Transpose,
      Mesh->False,
      Frame->False,
      ColorFunction->(Hue[0,#,1]&),
      Epilog->
       {Text[FontForm[PaddedForm[tmax-k,2],
                      {"Helvetica", 24}],
             {nx/2, 0.9*ny}],
        Thickness[.02],
        Line[{{0,0.9*ny},{0,0},{nx,0},{nx,0.9*ny}}]}],
  {k, tmax, 0, -1}]

It takes about 10 minutes for the above to produce, and then render, the 60 frames. Most of this time is taken up by ListDensityPlot and very little by fire.

To use the original non-compiled code (which is very slow) just replace the line: 
fire[None,nx,ny,{-2,2},{0,1.7},rands,k]; a=Peek[1]; 
with the line
fireCode[a,nx,ny,{-2,2},{0,1.7},rands,k];

The reason for the slight difference in syntax is that in some cases you may not always want a copy of the argument a back at Mathematica's end. So you have to pull it back manually with the Peek command. This may change in InterCall version 3.

(
nx = ny = 121;
fire[None,nx,ny,{-2,2},{0,1.7},rands,0]; a=Peek[1];
ListDensityPlot[a//Transpose,
      Mesh->False,
      Frame->False,
      ColorFunction->(Hue[0,#,1]&),
      Epilog->{Text[FontForm["Double-click here for",
                             {"Helvetica", 18}],
                   {nx/2, 0.9*ny}],
               Text[FontForm["FIRE ANIMATION",
                             {"Helvetica", 18}],
                   {nx/2, 0.8*ny}],
               RGBColor[0,0,1],
               Text[FontForm["(Created using InterCall 2.0)",
                             {"Geneva", 10}],
                   {nx/2, 0.7*ny}],
               RGBColor[0,0,0],
               Thickness[.02],
               Line[{{0,0.9*ny},{0,0},{nx,0},{nx,0.9*ny}}]}];1;
nx = ny = 61; tmax = 60;
Do[
 fire[None,nx,ny,{-2,2},{0,1.7},rands,k]; a=Peek[1];
 ListDensityPlot[a//Transpose,
      Mesh->False,
      Frame->False,
      ColorFunction->(Hue[0,#,1]&),
      Epilog->
       {Text[FontForm[PaddedForm[tmax-k,2],
                      {"Helvetica", 24}],
             {nx/2, 0.9*ny}],
        Thickness[.02],
        Line[{{0,0.9*ny},{0,0},{nx,0},{nx,0.9*ny}}]}],
  {k, tmax-1, 0, -1}]);

Acknowledgements

The fireCode texture that is implemented here, is based on ideas from Flip Phillips. Unfortunately I don't know his address or any other details about him – please contact me if you know his email address. He did however credit these sources:

Computer Graphics, Siggraph 85 Conference Proceedings, 19, 3, July 1985

Peachy, Darwin, Solid Texturing of Complex Surfaces

Perlin, Ken, An Image Synthesizer

About InterCall

For more information on InterCall send email to intercall_forward@wri.com. An order form is available via MathSource – email the line send 0202-587-0011 to mathsource@wri.com for details. Information can also be posted or faxed to Analytica below.

Analytica                     Fax: +61 (9) 386 5666
P.O. Box 522
Nedlands 6009
Perth WA
AUSTRALIA.

Triceratops