Wave Space
WHAT IS IT?
This model simulates sound wave propagation and interference in a two-dimensional space (the "membrane"). By default, this space is rectangular (i.e. the StarLogo canvas), but the drawing tools can be used to draw sound boundaries (which echo most of the sound, and do not let any propagate through) and insulators (which absorb most sound energy, but let some propagate through and some echo back). One or more driver turtles can be placed in various locations, and a listener turtle can be moved to any location, with the wave at that point plotted on a graph.
HOW IT WORKS
The driver turtles have a wave-z variable, which changes over time, as follows:
wave-z[t] = driver-amplitude * sin(t * 360 / driver-period)
At each step, each driver turtle sets the z variable of the patch on which it stands to value of its own wave-z variable. Patches have z and velocity variables, which propagate as follows.
For membrane (blue) patches:
velocity[t] = (1 - friction/100) * (velocity[t-1] + stiffness/100 * (z[north[t-1]] + z[east[t-1]] + z[south[t-1]] + z[west[t-1]] - 4 * z[t-1])) z[t] = z[t-1] + velocity[t]
For insulation (brown) patches:
z[t] = (z[north[t-1]] + z[east[t-1]] + z[south[t-1]] + z[west[t-1]]) / 4
For boundary (yellow) patches:
z[t] = 0
(At each step, the sum of the z values of the neighboring patches - i.e. z[north[t-1]], z[east[t-1]], etc. - is computed with the nsum4 command.)
In physical terms, a force is applied to each patch, proportional to the difference between the patch's height and the average height of the neighboring four patches. This force changes the velocity of each patch; the velocity is damped by friction. This is analogous to having the canvas made up as a lattice of springs, with each spring connecting two adjacent neighbors. This analogy also helps make it clear why, in this model, stiffness has a maximum value of 50%: if it were higher, each intersection in the lattice would tend to "overshoot" the balance point with its neighbors, and the membrane would oscillate out of control very easily.
At each step, following the propagation calculations described above, the listener turtle "hears" the z value of the patch on which it stands.
HOW TO USE IT
1. Click the "Create Tank" button to create a basic tank, with boundaries around the perimeter, a listener turtle, and a single driver turtle.
2. Use the drawing tools to color patches yellow (for boundaries) or brown (for insulation); any other colors will be ignored (i.e. treated as membrane).
3. Add and/or remove drivers with the "Add Driver" and "Remove Driver" buttons. Both buttons operate as follows: press the button and click the mouse on the canvas; a driver will be added (or removed) in the patch clicked. Note that a driver cannot be created on a patch which already has a driver (drivers can be moved to the same patch later, but this does not increase the amplitude of the resulting wave), on the perimeter of the canvas, or on any yellow (edge) patch.
4. Use the mouse to move the driver and listener turtles as desired.
5. Set the wave propagation properties with the sliders. Note that "Period" is used instead of frequency (to avoid having to deal with scale issues right away); the period is expressed in terms of steps per cycle. The "Stiffness" and "Friction" slider values are interpreted according to the propagation formulas shown above.
6. Run the model with the "Go!" button; the button will remain depressed (with the model running) until it is clicked again - or until the "Create Tank" button is clicked again.
7. At anytime (whether the model is stopped or running), any or all of steps 2 through 5 can be repeated. After changing the model properties, it may be difficult to see the effects clearly, in the tank and/or on the plot; the "Reset Tank" and "Reset Plot" buttons can be used to clear the waves in the tank and plot, or just in the plot (respectively).
(As the model is running, you can use the "Shading Limit" slider to control how patches are shaded in the tank; in general, a value that is 75% of the driver amplitude is a good starting value for this slider.)
THINGS TO NOTICE
Note that, even with a single driver, the echoes from the tank walls will interact with the original wave, producing interference patterns.
The effects of changes to the "Stiffness" parameter can be counterintuitive: we tend to think that more flexible media conduct waves better, but that seems not to be the case in the model. In fact, the model's behavior corresponds to what happens in reality - for example, sound travels much more effectively (and quickly) through steel than through rubber.
Observe how the propagation of average neighbors' height to the insulation patches tends to damp waves - reducing echo from and conduction through the insulation - by minimizing the sum of the differences in height between the insulation and its neighbors - and thus minimizing the change of velocity on the neighboring patches. On the other hand, maintaining the edge patches at a height of zero tends to continue wave motion in the neighboring patches, by accelerating them towards the zero height.
Notice that even at a friction level of 10 (i.e. wave velocity is damped by 10%), the wave motion - at least, the amplitude - drops off very quickly.
THINGS TO TRY
Start with the default placement of a single driver, and try to create standing waves; move the listener around to the locations that look like nodes, and check to see if the plotted wave is quiet (i.e. has a low amplitude).
Change the driver period; do you still have standing waves?
Add insulation to one wall, and see what changes. Can you still create standing waves?
Add a second driver, and try to position the drivers and the listener so that the waves from the two drivers reach the listener as close to out of phase as possible - so that the listener experiences a wave with very little amplitude.
Now, try to position the drivers and the listener, to maximize the amplitude of the wave at the listener's location.
Try to create a "sound-proof booth" for the listener. For this, use a driver amplitude of 30, and consider your design successful if the listener wave has a maximum amplitude of 1. Make sure your design works even if the sound-proof booth is in a loud part of the tank.
Can your sound-proof booth be made with less insulation? What if insulation is very expensive?
Change the shape of the tank, by drawing additional boundary lines. Try to find a shape that minimizes standing waves (i.e. try to reduce or eliminate dead spots); use insulation along some of the walls to help with this. Now, change the driver period, and see if your design is still effective at minimizing dead or quiet spots.
Try adding one or more (keep it to a few) drivers, to make the wave amplitude as consistent as possible, throughout the tank.
Can you find a layout of the boundaries, insulators, and drivers, which seems to work well for several different values of the driver period?
EXTENDING THE MODEL
Driver turtles already have a variable called "delay", which can be used to offset the wave produced by a driver; this can be used to compensate for the fact that a listener may hear the wave produced by one driver before that produced by another. (Concert venues regularly do this - delaying the signal sent to speakers further from the stage, so that audients who can hear the sound from multiple speakers do not hear them out of synch.) However, this model doesn't provide any convenient way to change this delay value, besides inspecting a driver turtle and modifying the delay value directly. One possible extension would be to construct a way to modify this delay more directly, and incorporate this concept into activities.
Another extension would allow for drivers to have independent amplitudes (or even independent periods). This would complicate the user interface, but wouldn't have much impact on the wave generation: the "drive" procedure would simply refer to turtle variables, instead of globals.
Different tank layouts could be saved (with the "export-picture-patches-name" command), and buttons could be created - each of which would load one of these layouts on demand (using "import-picture-name").
STARLOGO FEATURES
This model relies heavily on the NSum4 command, which provides a convenient mechanism for propagating information across patches. In this case, this is an essential element of the wave propagation.
The z value of each patch is represented in the two-dimensional StarLogo canvas by scaling the membrane color: dark for the wave troughs, and light for the wave peaks.
The "wait-until" and "mouse-down?" commands are used in procedures called by the "Add Driver" and "Remove Driver" buttons, to wait for a mouse click event; the "mouse-xcor" and "mouse-ycor" commands are then used to detect the location of the mouse click, and - assuming the mouse click is in a valid spot - create or kill a driver turtle. The implementation of this functionality permits these buttons to be used whether the model is running or stopped.
The "case" command is used to distinguish between patch colors, and execute the propagation logic accordingly.
Global variables are used for all of the colors (and color shade groups) used in the model, so that the colors used for boundaries, insulation, and the membrane can be changed easily.
The "no-display" and "display" commands are used at the start and end of the "create-tank" procedure, to minimize the time required for (and the visual distraction of) drawing the basic wave tank.
CREDITS AND REFERENCES
The basics of this model were inspired by the Wave Machine model (copyright 1996 by Uri Wilensky) included in the NetLogo v3 Model Library (http://ccl.northwestern.edu/netlogo/models/WaveMachine). In particular, the calculations by which the wave propagates through the membrane in this model are very similar to those used in the NetLogo model (although this model uses patches extensively, and the NetLogo model uses turtles for the membrane lattice).
This model was written by Nick Bennett, Grass Roots Consulting (nickbenn@g-r-c.com), for use in an Acoustics section of the "Computer-based Exploration of the Science Behind the Headlines" course, at the Monte del Sol Charter School, Santa Fe, NM. The facilities for engineering the wave space (e.g. drawing boundaries & insulation, adding & moving drivers) were refined in an Adventures in Modeling workshop at the Santa Fe Institute.
Turtle procedures
; There are two breeds of turtles: "drivers" that generate sound waves, and
; "listeners" that receive the sound waves, and drive a graphic plot. In
; general, only one of the latter is created, while one or more of the former
; may be created.
breeds [
drivers
listeners
]
; For convenience in plotting, driver turtles set their wave-z value based on
; sinusoidal motion, and then set the patch z value from wave-z; listener
; turtles, on the other hand, take their wave-z value from the z value of the
; patch. Driver turtles can also have a wave delay offset; this corresponds to
; using a delay to compensate for speaker positioning, to reduce phase
; interference. (Currently, this delay can only be set by inspecting a driver
; turtle.)
turtles-own [
wave-z
delay
]
; Place a driver turtle on the horizontal line through the middle of the canvas,
; halfway between the left edge and the center.
to initialize-driver
initialize-driver-at (0 - round (screen-half-width / 2.0)) 0
end
; Place a driver turtle at the specified location, after setting the color,
; shape, and wave-z location.
to initialize-driver-at :x :y
setxy :x :y
setcolor driver-color
setshape bubble-shape-solid-color
set wave-z 0
end
; Place a listener turtle on the horizontal line through the middle of the
; canvas, halfway between the right edge and the center.
to initialize-listener
initialize-listener-at (screen-half-width / 2) 0
end
; Place a listener turtle at the specified location, after setting the color,
; shape, and wave-z location.
to initialize-listener-at :x :y
setxy :x :y
setcolor listener-color
setshape person-shape
set wave-z 0
end
; Set the wave-z value of the turtle, and the z value of the patch, based on
; sinusoidal motion with the specified period.
to drive
set wave-z (driver-amplitude *
(sin ((clock - delay) * step-angle + step-offset)))
set z wave-z
scale-color driver-color wave-z (0 - driver-amplitude) driver-amplitude
end
; Set the wave-z value of the turtle from the z value of the underlying patch.
to listen
set wave-z z
end
Observer procedures
; Globals are used to keep track of the current iteration count, step angle, and
; step offset, and as named constants for the various colors.
globals [
clock
step-angle
step-offset
driver-color
listener-color
edge-color
edge-color-group
membrane-color
membrane-color-group
insulator-color
insulator-color-group
]
; Each patch has a height and velocity; and, in each iteration, the average
; neighbor's height is computed by summing the z values into neighbor-z.
patches-own [
z
velocity
neighbor-z
]
; Create a simple tank, with a boundary at the perimeter, a listener turtle,
; and a single driver turtle.
to create-tank
no-display
stopgo!
clearall
startup
mark-edges
reset-tank
initialize-turtles
display
end
; Reset the clock, "kill" any current waves (i.e. reset the z value of each
; patch to 0), and clear the plot - but maintain all existing boundaries and
; insulation.
to reset-tank
set clock 0
reset-patches
reset-turtles
clearplots
end
; Create the listener turtle and a single driver turtle (more can be added
; with the add-driver procedure).
to initialize-turtles
create-listeners-and-do 1 [
initialize-listener
]
create-drivers-and-do 1 [
initialize-driver
]
end
; Reset the listener and driver turtles (e.g. after killing any waves in the
; tank), but don't change their locations.
to reset-turtles
ask-drivers [
initialize-driver-at xcor ycor
]
ask-listeners [
initialize-listener-at xcor ycor
]
end
; Set the values of the color globals.
to startup
set driver-color red
set listener-color green
set edge-color yellow
set edge-color-group (edge-color div 10)
set membrane-color blue
set membrane-color-group (membrane-color div 10)
set insulator-color brown
set insulator-color-group (insulator-color div 10)
setbg membrane-color
end
; Add a driver turtle to the model, based on mouse click location. A driver
; cannot be added in a patch where there is already a driver (or listener), or
; on the perimeter of the canvas.
to add-driver
wait-until [mouse-down?]
if (((abs mouse-xcor) < screen-half-width)
and ((abs mouse-ycor) < screen-half-height)
and (((pc-at mouse-xcor mouse-ycor) div 10) != edge-color-group)
and ((count-turtles-at mouse-xcor mouse-ycor) = 0)) [
create-drivers-and-do 1 [
initialize-driver-at mouse-xcor mouse-ycor
]
]
end
; Delete a driver turtle from the model, based on mouse click location.
to delete-driver
wait-until [mouse-down?]
if (((abs mouse-xcor) <= screen-half-width)
and ((abs mouse-ycor) <= screen-half-height)
and ((count-drivers-at mouse-xcor mouse-ycor) > 0)) [
kill one-of-drivers-at mouse-xcor mouse-ycor
]
end
; Return all patches to a z level of 0, velocity of 0, and the
; base color for the color group of patch type.
to reset-patches
ask-patches [
let [:color-group (pc div 10)]
set z 0
set velocity 0
case (:color-group) [
edge-color-group [
setpc edge-color
]
insulator-color-group [
setpc insulator-color
]
true [
setpc membrane-color
]
]
]
end
; Mark the perimeter of the canvas as a boundary (edge).
to mark-edges
ask-patches-with [((abs xcor) = screen-half-width)
or ((abs ycor) = screen-half-height)] [
setpc edge-color
]
end
; Execute a single iteration of the model, by calculating the "angular distance"
; of the current step (sinusoidal motion has a period of 360 degrees; the user
; sets the period of the motion in the model with driver-period, so the angular
; distance of the step is 360/driver-period), calculating an angular offset
; (this ensures that we will hit the peak of the wave at least once per cycle,
; even with a period that doesn't divide evenly into 90 degrees), propagating
; the wave pattern, and then incrementing the clock.
to go
set step-angle (360.0 / driver-period)
set step-offset (90 mod step-angle)
propagate
set clock (clock + 1)
end
; For membrane patches, propagate the wave pattern by summing each patch's
; neighbors' heights - using this to compute the new velocity (the change in
; velocity is proportional to the sum of the differences between the patch
; height and that of its neighbors, and to the elasticity of the membrane; the
; resulting velocity is damped by the friction of the membrane), and finally the
; new height of the patch. For insulation patches, simply set the new height to
; the average of the neighbors' heights; this tends to damp wave motion, by
; decreasing the change in patch velocity of the neighboring patches. For
; boundary patches, maintain the height at 0; this tends to cause wave motion to
; echo, by accelerating neighboring patches toward a z value of 0.
to propagate
let [:stiffness-factor (membrane-stiffness / 100.0)]
let [:friction-factor ((100 - membrane-friction) / 100.0)]
ask-drivers [
drive
]
nsum4 z neighbor-z
ask-patches [
case (pc div 10) [
edge-color-group [
set z 0
]
insulator-color-group [
set z (neighbor-z / 4.0)
]
true [
set velocity (:friction-factor *
(velocity + :stiffness-factor * (neighbor-z - 4 * z)))
set z (z + velocity)
scale-pc membrane-color z (0 - scaling-limit) scaling-limit
]
]
]
ask-listeners [
listen
]
end
