After deciding to use a fullscreen video as the landing for my homepage I had several design goals for the video in mind:

  • it should be created from scratch and representative of science I work on
  • it should be simple with strong visuals so the viewer is not distracted by trying to understand what is going on
  • things should be changing at a fast enough rate to be interesting but slow enough to be relaxing (to me this feels like a timescale of 5-10 seconds)
  • though it doesn’t have to perfectly loop, it should be in some sort of steady state so that it can loop and not be jarringly obvious
  • it should be at least 30 seconds long

I settled on running a simulation of driven turbulence using the Athena++ code. Running the simulation is as easy as installing Athena++ and fftw, compiling with python configure.py --problem=turb --eos=isothermal -fftw, and then running the binary with input file athinput.turb.

I used the following parameters to get the kind of turbulence that would meet my design goals:

<turbulence>
dedt       = 1.0  # Energy injection rate (for driven) or Total energy (for decaying)
nlow       = 2    # cut-off wavenumber at low-k
nhigh      = 512  # cut-off wavenumber at high-k
expo       = 2.0  # power-law exponent
tcorr      = 0.1  # correlation time for OU process (both impulsive and continuous)
dtdrive    = 0.1  # time interval between perturbation (impulsive)
f_shear    = 0.5  # the ratio of the shear component
rseed      = -1   # if non-negative, seed will be set by hand (slow PS generation)

<problem>
turb_flag  = 3    # 1 for decaying, 2 (impulsive) or 3 (continuous) for driven turbulence

But most notably I set nlow = 2 and nhigh=512 to seed intermediate scale modes and turb_flag = 3 to drive the turbulence to a steady state. After putting the Athena++ outputs into the directory data/, converting them to .png was done with a simple python script:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import athena_read
from PIL import Image
for i in range(0,2000):
    j  = 800 + 3*i
    # choose colormap
    cmap = mpl.cm.get_cmap('OrRd')
    # choose normalization
    norm = mpl.colors.Normalize(0.3,2.0)
    x,y,z,data = athena_read.vtk('data/Turb.block0.out2.'+str(j).zfill(5)+'.vtk')
    # normalize data to interval [0,1]
    normed_data = norm(data['dens'][0])[::-1,:]
    # get rgb colors from normed, cmapped data
    rgb = cmap(normed_data,bytes=True)[:,:,:3]
    # create image
    im = Image.fromarray(rgb)
    im.save('pngs/turb.'+str(i)+'.png')

I wanted to start with simulation already in steady-state and save on webpage loading times by making the video 20 fps, hence starting at file 800 only reading every third file (j = 800 + 3*i). With the .png frames in hand, I converted them to a video with an ffmpeg command:

ffmpeg -f image2 -r 20 -i turb.%d.png -vcodec libx264 -crf 18 -pix_fmt yuv420p homepage-turbulence.mp4