load "delaunay";
load "airflow-window";
load "conjugate-gradient";
load "airflow-utils";
% ************************************* %
% THE MAIN PROGRAM %
% ************************************* %
function airflow_demo(ignore) =
let
% Generate the window %
display = get_environment_variable("DISPLAY");
win = w_make_window(((50,0),(735,685)),"Fluid Flow",w_cyan,display);
graph_files = nesl_path ++ "examples/airfoil";
(points,edges) = read_graph(graph_files);
bounding_box = w_bounding_box(points);
win,box = w_add_box(((110,120),(600,540)),bounding_box,"mesh",w_white,win);
win = w_add_button(((25,120),(60,30)),"quit",w_red,win);
win = w_add_button(((25,170),(60,30)),"zoom up",w_red,win);
win = w_add_button(((25,220),(60,30)),"step",w_red,win);
win,tbox = w_add_text_box(((25,32),(150,56)),"help",w_cyan,win);
win,tbox = w_add_text_box(((190,22),(520,76)),"note",w_white,win);
% INTRO %
window_state = empty_window_state(win,bounding_box);
ignore = write_note("
This is an algorithm animation of using the control volume method to simulate
fluid flow over a wing. It first shows how to set up the linear equations
and then solves them using Conjugate Gradient.@
Any comments are welcome.",
window_state);
window_state = add_segments(zip(points,points),1,w_dark_gray,window_state);
fl,window_state = wait_for_step(window_state);
in if not(fl) then w_kill_window(win) else let
% DELAUNAY TRIANGULATION %
window_state = empty_window_state(win,bounding_box);
ignore = write_note("
First we generate the Delaunay triangulation of the set of points.",
window_state);
delaunay = delaunay_from_edgelist(points,edges);
segs = get_from_edges(edges,points);
window_state = add_segments(segs,1,w_dark_gray,window_state);
fl,window_state = wait_for_step(window_state);
in if not(fl) then w_kill_window(win) else let
% VORONOI DIAGRAM %
ignore = write_note("Based on the Delaunay triangulation, we generate
the Voronoi diagram of the points. The Voronoi edges are clipped
so they don't extend outside the boundaries.
This clipping is important for the next step.",window_state);
voronoi = voronoi_edges_from_delaunay(points,delaunay);
segs = flatten({{seg: x,seg in cell}: cell in voronoi});
segs = clean_segments(segs);
window_state = add_segments(segs,1,w_blue,window_state);
fl,window_state = wait_for_step(window_state);
in if not(fl) then w_kill_window(win) else let
% SET UP EQUATIONS %
ignore = write_note("We generate a set of linear equations in
terms of the Flow Potential at each node. The equation for a node
sets the net flow into its voronoi cell to zero.
This is done by estimating the flow through
each Voronoi edge (green) to be the length of the edge times
the gradient of the flow potential along its Delaunay
edge (red).",window_state);
a = {cell_equation(i,x0,x1s,cell)
: cell in voronoi
; x1s in nested_get(points,delaunay)
; i in [0:#voronoi]
; x0 in points};
example_point = 1349;
segs = get_from_edges({example_point,n:
n in delaunay[example_point]},points);
window_state = add_segments(segs,4,w_red,window_state);
segs = {seg: x,seg in voronoi[example_point]};
window_state = add_segments(segs,4,w_green,window_state);
fl,window_state = wait_for_step(window_state);
in if not(fl) then w_kill_window(win) else let
% SET UP BOUNDARY CONDITIONS %
ignore = write_note("For the boundary cells we set
the flow into the cell to be proportional to the component of
its boundary edges perpendicular to the X axis.@
We also set the potential at one boundary node to 0 so that we have
a reference value (the black point on the upper right).",window_state);
segs = get_from_edges({i,j: i in outside_boundary;
j in rotate(outside_boundary,1)},points);
b,arrows = generate_bs(outside_boundary,points);
sink_point = max_val(outside_boundary);
a = rep(a,[(sink_point,1.0)],sink_point);
b = rep(b,0.0,sink_point);
arrows = {center,(.06,0.): center in arrows};
window_state = add_segments(segs,2,w_green,window_state);
window_state = add_arrows(arrows,w_red,window_state);
window_state = add_points([(0.0,points[sink_point])],window_state);
fl,window_state = wait_for_step(window_state);
in if not(fl) then w_kill_window(win) else let
% SOLVE THE EQUATIONS USING CONJUGATE GRADIENT %
ignore = write_note("We now solve the linear equations using the
Conjugate Gradient iterative method. The colors represent the Flow
Potential and the flow velocity is the gradient of this potential.
Each pass represents 150 iterations of the Conjugate Gradient method.",
window_state);
segs = get_from_edges(edges,points);
window_state = empty_window_state(win,bounding_box);
window_state = add_segments(segs,1,w_dark_gray,window_state);
xx = cgsolinit(a,b);
window_state,x = multisolve(a,b,xx,0,3,150,points,window_state);
fl,Window_state = wait_for_step(window_state);
in if not(fl) then w_kill_window(win) else let
% SHOW FLOWS %
ignore = write_note("We now generate the flow amplitude and direction
for each Delaunay triangle. This is done by approximating the gradient
of the flow potential at the center of the triangle by looking
at the value of the potential at the three corners. The length of the
arrows represent the amplitude.",window_state);
segs = get_from_edges(edges,points);
window_state = empty_window_state(win,bounding_box);
window_state = add_segments(segs,1,w_dark_gray,window_state);
triangles = delaunay_triangles_from_edges(delaunay);
arrows = {gradient_from_triangle(v,p):
v in get_from_triangles(triangles,x);
p in get_from_triangles(triangles,points)};
window_state = add_arrows(arrows,w_purple,window_state);
fl,Window_state = wait_for_step(window_state);
in if not(fl) then w_kill_window(win) else let
% END OF DEMO %
ignore = write_note("The demo is done: the next step will quit.",
window_state);
fl,Window_state = wait_for_step(window_state);
in w_kill_window(win) $
%
set arg_check off;
dump vcode "/afs/cs/project/scandal/fun/src/airflow.vcode" airflow_demo(0);
%