is a programming language for writing physical simulations. Ebb programs are performance portable: they can be efficiently executed on both CPUs and GPUs. Ebb is embedded in the Lua programming language using Terra, which can itself be embedded in C/C++ programs as a library.

Ebb code looks like this:

import "ebb" local L = require "ebblib" -- load the triangle mesh local ioOff = require 'ebb.domains.ioOff' local PN = require 'ebb.lib.pathname' local mesh = ioOff.LoadTrimesh( PN.scriptdir() .. 'bunny.off' ) -- define globals and constants local timestep = L.Constant(L.double, 0.45) local K = L.Constant(L.double, 1.0) local max_change = L.Global(L.double, 0.0) -- define fields: temperature and change in temperature mesh.vertices:NewField('t', L.double):Load(function(index) if index == 0 then return 3000.0 else return 0.0 end end) mesh.vertices:NewField('d_t', L.double):Load(0.0) -- functions executed for-each vertex local ebb compute_diffusion ( v : mesh.vertices ) var count = 0.0 for nv in v.neighbors do v.d_t += timestep * K * (nv.t - v.t) count += 1.0 end v.d_t = v.d_t / count end local ebb apply_update ( v : mesh.vertices ) v.t += v.d_t max_change max= L.fabs(v.d_t) v.d_t = 0.0 end -- the simulation loop for i = 1,300 do if i % 30 == 0 then max_change:set(0.0) end mesh.vertices:foreach(compute_diffusion) mesh.vertices:foreach(apply_update) if i % 30 == 0 then print('iter #'..i, max_change:get()) end end

Adding visualization routines, (in repository version) we can see the result of the above simulation.

Ebb was designed with a flexible data model that allows for encoding a range of different domains. As a non-exhaustive list, Ebb supports triangle meshes, grids, tetrahedral meshes, and particles. For example, here is a similar heat diffusion program written for a grid:

import 'ebb' local L = require 'ebblib' local GridLib = require 'ebb.domains.grid' local N = 40 local grid = GridLib.NewGrid2d { size = {N, N}, origin = {-N/2, -N/2}, width = {N, N}, periodic_boundary = {true,true}, } -- define constants, globals and fields local timestep = L.Constant(L.double, 0.45) local conduction = L.Constant(L.double, 1.0) local max_diff = L.Global(L.double, 0.0) grid.cells:NewField('t', L.double):Load(function(x_idx, y_idx) if x_idx == 4 and y_idx == 6 then return 1000 else return 0 end end) grid.cells:NewField('new_t', L.double):Load(0) -- compute diffusion local ebb update_temperature ( c : grid.cells ) var avg = (1.0/4.0) * ( c(1,0).t + c(-1,0).t + c(0,1).t + c(0,-1).t ) var diff = avg - c.t c.new_t = c.t + timestep * conduction * diff end -- measure statistic local ebb measure_max_diff ( c : grid.cells ) var avg = (1.0/4.0) * ( c(1,0).t + c(-1,0).t + c(0,1).t + c(0,-1).t ) var diff = avg - c.t max_diff max= L.fabs(diff) end -- simulation loop for i=1,360 do grid.cells.interior:foreach(update_temperature) grid.cells:Swap('t', 'new_t') if i % 10 == 0 then -- measure statistics every 10 steps max_diff:set(0) grid.cells.interior:foreach(measure_max_diff) print( 'iteration #'..tostring(i), 'max gradient: ', max_diff:get() ) end end

Again, we can visualize this simulation

Furthermore, domain libraries are user-authorable and can be coupled together in user code. For example, Ebb seamlessly supports coupling particles to a grid, or coupling the vertices of a mesh to a grid. By adding the following code to the preceding grid-based heat diffusion, we can set up a particle advection driven by the heat gradient.

-- create and initialize particle relation local particles = L.NewRelation { name = "particles", size = N*N, } local particle_positions = {} for yi=0,N-1 do for xi=0,N-1 do particle_positions[ xi*N + yi + 1 ] = { xi + 0.5, yi + 0.5 } end end particles:NewField('pos', L.vec2d):Load(particle_positions) -- establish link from particles to cells particles:NewField('cell', grid.cells) grid.locate_in_cells(particles, 'pos', 'cell') -- define particle advection local ebb wrap( x : L.double ) return L.fmod(x + 100*N, N) end local ebb advect_particle_position ( p : particles ) -- estimate heat gradient using a finite difference var c = p.cell var dt = { c(1,0).t - c(-1,0).t, c(0,1).t - c(0,-1).t } -- and move the particle downwards along the gradient var pos = p.pos - 0.1 * timestep * dt -- wrap around the position... p.pos = { wrap(pos[0]), wrap(pos[1]) } end

To advect the particles, we add two lines to the simulation loop:

for i=1,360 do grid.cells:foreach(update_temperature) grid.cells:Swap('t', 'new_t') particles:foreach(advect_particle_position) grid.locate_in_cells(particles, 'pos', 'cell') if i % 10 == 0 then -- measure statistics every 10 steps ... end end

A visualization of the advection

The tutorials and code repository contain both simpler and more elaborate examples, explained in more detail.

Liszt is …

A project at Stanford University to develop domain-specific languages for physical simulation. Liszt is focused on performance portability. Performance portable programs are programs that can be run efficiently on a variety of different parallel systems/platforms/architectures. (e.g. CPU, GPU, Multi-core, Clusters, Supercomputers)

Documentation and artifacts for the original Liszt language can be found online.

Ebb is the primary DSL for the Liszt project, with specialized DSLs for collision detection and other problems in the works.

Ebb contributors

Gilbert Bernstein Chinmayee Shah Crystal Lemire Matthew Fisher Zach Devito Phil Levis Pat Hanrahan