GasLab With Sound
Model was written in NetLogo 5.0.4
•
Viewed 341 times
•
Downloaded 78 times
•
Run 1 time
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
Comments and Questions
Click to Run Model
extensions [ sound ] globals [ tick-delta ;; how much we advance the tick counter this time through max-tick-delta ;; the largest tick-delta is allowed to be box-edge ;; distance of box edge from axes pressure pressure-history zero-pressure-count ;; how many zero entries are in pressure-history wall-hits-per-particle ;; average number of wall hits per particle particles-to-add new-particles ;; agentset of particles added via add-particles-middle message-shown? ;; whether we've shown the warning message yet length-horizontal-surface ;; the size of the wall surfaces that run horizontally - the top and bottom of the box length-vertical-surface ;; the size of the wall surfaces that run vertically - the left and right of the box three-speed ;; current speed of particle 3 (music addition) pressure-now ;; current pressure (music addition ] breed [ particles particle ] breed [ flashes flash ] flashes-own [birthday] particles-own [ speed mass ;; particle info wall-hits ;; # of wall hits during this clock cycle ("big tick") momentum-difference ;; used to calculate pressure from wall hits last-collision new? ;; used to build the new-particles agentset; this is ;; only ever set to true by add-particles-middle ] to startup set message-shown? false end to setup sound:stop-music ;; start music by closing previous one let tmp message-shown? ca set message-shown? tmp set-default-shape particles "circle" set-default-shape flashes "square" set particles-to-add 0 ;; box has constant size... set box-edge (max-pxcor - 1) ;;; the length of the horizontal or vertical surface of ;;; the inside of the box must exclude the two patches ;; that are the where the perpendicular walls join it, ;;; but must also add in the axes as an additional patch ;;; example: a box with a box-edge of 10, is drawn with ;;; 19 patches of wall space on the inside of the box set length-horizontal-surface ( 2 * (box-edge - 1) + 1) set length-vertical-surface ( 2 * (box-edge - 1) + 1) make-box make-particles initial-number set pressure-history [] set zero-pressure-count 0 reset-ticks set pressure-now pressure ;; setup for current pressure set three-speed [speed] of particle 3 ;; setup for current particle speed end to go if not single-particle-speed? and three-speed > 0 ;; making sure that particle speed is sung only when switch is on [ sound:stop-note "oboe" (80 - 120 / ( three-speed )) sound:stop-note "oboe" 50 ] if not pressure? and pressure > 0 ; making sure that pressure is sung only when switch is on [ sound:stop-note "recorder" ( 100 - 2000 / pressure ) sound:stop-note "recorder" 20 ] if single-particle-speed? [ ask particle 3 ;; asking particle 3 to sing, changes note when speed changes, taking care of clock = 0 [ ifelse ((abs (speed - three-speed) > 0 ) ) or (abs (speed - three-speed ) = speed ) [ sing-one-particle-speed ] [ ifelse ticks > 0 [ stop ] [ sound:start-note "oboe" (80 - 120 / ( [speed] of particle 3 )) single-particle-loudness - 10] ] ] ] if real-time? [ sing-real-time ] ;; sing the real time ticker ask particles [ set new? false ] ask particles [ bounce ] ask particles [ move ] ask particles [ check-for-collision ] add-particles-side tick-advance tick-delta if floor ticks > floor (ticks - tick-delta) [ ifelse any? particles [ set wall-hits-per-particle mean [wall-hits] of particles ] [ set wall-hits-per-particle 0 ] ask particles [ set wall-hits 0 ] calculate-pressure update-plots if model-time? [ sound:play-note "nylon string guitar" 26 83 0.1 ] ] ;; sing model time calculate-tick-length ask flashes with [ticks - birthday > 0.4] [ set pcolor yellow die ] ifelse (single-particle-speed? or single-particle-collisions? or single-particle-wall-hits? ) ;; single particle speed is traced [ ask particle 3 [ pd ] ] [ ask particle 3 [ pu ] ] fade-patches ;; if the single particle is tracing, then the trace disappears after a while end to calculate-tick-length ifelse any? particles with [speed > 0] [ set tick-delta 1 / (ceiling max [speed] of particles) ] [ set tick-delta 1 ] end ;;; Pressure is defined as the force per unit area. In this context, ;;; that means the total momentum per unit time transferred to the walls ;;; by particle hits, divided by the surface area of the walls. (Here ;;; we're in a two dimensional world, so the "surface area" of the walls ;;; is just their length.) Each wall contributes a different amount ;;; to the total pressure in the box, based on the number of collisions, the ;;; direction of each collision, and the length of the wall. Conservation of momentum ;;; in hits ensures that the difference in momentum for the particles is equal to and ;;; opposite to that for the wall. The force on each wall is the rate of change in ;;; momentum imparted to the wall, or the sum of change in momentum for each particle: ;;; F = SUM [d(mv)/dt] = SUM [m(dv/dt)] = SUM [ ma ], in a direction perpendicular to ;;; the wall surface. The pressure (P) on a given wall is the force (F) applied to that ;;; wall over its surface area. The total pressure in the box is sum of each wall's ;;; pressure contribution. to calculate-pressure ;; by summing the momentum change for each particle, ;; the wall's total momentum change is calculated set pressure 15 * sum [momentum-difference] of particles set pressure-history lput pressure pressure-history set zero-pressure-count length filter [? = 0] pressure-history ask particles [ set momentum-difference 0 ] ;; once the contribution to momentum has been calculated ;; this value is reset to zero till the next wall hit ifelse pressure? ;; when pressure changes, it sings [ sing-pressure ] [ ifelse pressure-now > 0 [ sound:stop-note "recorder" ( 100 - 2000 / pressure-now ) sound:stop-note "recorder" ( 30 ) ] [ stop ] ] end to bounce ;; particle procedure let tone heading ;; if we're not about to hit a wall (yellow patch), or if we're already on a ;; wall, we don't need to do any further checks if shade-of? yellow pcolor or not shade-of? yellow [pcolor] of patch-at dx dy [ stop ] ;; get the coordinates of the patch we'll be on if we go forward 1 let new-px round (xcor + dx) let new-py round (ycor + dy) ;; if hitting left or right wall, reflect heading around x axis if (abs new-px = box-edge) [ set heading (- heading) set wall-hits wall-hits + 1 if (wall-hits?) [ sound:play-note "celesta" tone wall-hits-loudness 0.15 ] ;; when a particle hits the wall there's sound (macro) if (single-particle-wall-hits? and who = 3) ;; when a particle hits the wall there's sound (micro) [ ask particle 3 [ pd ] sound:play-note "clavi" ( 30 + heading / 5 ) wall-hits-loudness 0.2 ] ;; if the particle is hitting a vertical wall, only the horizontal component of the speed ;; vector can change. The change in velocity for this component is 2 * the speed of the particle, ;; due to the reversing of direction of travel from the collision with the wall set momentum-difference momentum-difference + (abs (dx * 2 * mass * speed) / length-vertical-surface) ] ;; if hitting top or bottom wall, reflect heading around y axis if (abs new-py = box-edge) [ set heading (180 - heading) set wall-hits wall-hits + 1 if wall-hits? [ sound:play-note "celesta" heading single-particle-loudness 0.1 ] if (single-particle-wall-hits? and who = 3) [ ask particle 3 [ pd ] sound:play-note "clavi" ( 30 + heading / 5 ) single-particle-loudness 0.1 ] ;; if the particle is hitting a horizontal wall, only the vertical component of the speed ;; vector can change. The change in velocity for this component is 2 * the speed of the particle, ;; due to the reversing of direction of travel from the collision with the wall set momentum-difference momentum-difference + (abs (dy * 2 * mass * speed) / length-horizontal-surface) ] ask patch new-px new-py [ sprout-flashes 1 [ ht set birthday ticks set pcolor yellow - 3 ] ] end to move ;; particle procedure if patch-ahead (speed * tick-delta) != patch-here [ set last-collision nobody ] jump (speed * tick-delta) end to check-for-collision ;; particle procedure ;; Here we impose a rule that collisions only take place when there ;; are exactly two particles per patch. We do this because when the ;; student introduces new particles from the side, we want them to ;; form a uniform wavefront. ;; ;; Why do we want a uniform wavefront? Because it is actually more ;; realistic. (And also because the curriculum uses the uniform ;; wavefront to help teach the relationship between particle collisions, ;; wall hits, and pressure.) ;; ;; Why is it realistic to assume a uniform wavefront? Because in reality, ;; whether a collision takes place would depend on the actual headings ;; of the particles, not merely on their proximity. Since the particles ;; in the wavefront have identical speeds and near-identical headings, ;; in reality they would not collide. So even though the two-particles ;; rule is not itself realistic, it produces a realistic result. Also, ;; unless the number of particles is extremely large, it is very rare ;; for three or more particles to land on the same patch (for example, ;; with 400 particles it happens less than 1% of the time). So imposing ;; this additional rule should have only a negligible effect on the ;; aggregate behavior of the system. ;; ;; Why does this rule produce a uniform wavefront? The particles all ;; start out on the same patch, which means that without the only-two ;; rule, they would all start colliding with each other immediately, ;; resulting in much random variation of speeds and headings. With ;; the only-two rule, they are prevented from colliding with each other ;; until they have spread out a lot. (And in fact, if you observe ;; the wavefront closely, you will see that it is not completely smooth, ;; because some collisions eventually do start occurring when it thins out while fanning.) if count other particles-here = 1 [ ;; the following conditions are imposed on collision candidates: ;; 1. they must have a lower who number than my own, because collision ;; code is asymmetrical: it must always happen from the point of view ;; of just one particle. ;; 2. they must not be the same particle that we last collided with on ;; this patch, so that we have a chance to leave the patch after we've ;; collided with someone. let candidate one-of other particles-here with [who < [who] of myself and myself != last-collision] ;; we also only collide if one of us has non-zero speed. It's useless ;; (and incorrect, actually) for two particles with zero speed to collide. if (candidate != nobody) and (speed > 0 or [speed] of candidate > 0) [ collide-with candidate set last-collision candidate ask candidate [ set last-collision myself ] if collisions? ;; make a sound when there's a collision that is a function of the sum of their speeds (macro) [ sound:play-note "telephone ring" 2 * ([speed] of self + [speed] of candidate) collisions-loudness 0.15 ] if (single-particle-collisions? and ( who = 3 or [who] of candidate = 3 )) ;; make a sound when there's a collision that is a function of the sum of their speeds (micro) [ ask particle 3 [ pd ] sound:play-note "glockenspiel" 69 single-particle-loudness + 40 0.2 ] ] ] end ;; implements a collision with another particle. ;; ;; THIS IS THE HEART OF THE PARTICLE SIMULATION, AND YOU ARE STRONGLY ADVISED ;; NOT TO CHANGE IT UNLESS YOU REALLY UNDERSTAND WHAT YOU'RE DOING! ;; ;; The two particles colliding are self and other-particle, and while the ;; collision is performed from the point of view of self, both particles are ;; modified to reflect its effects. This is somewhat complicated, so I'll ;; give a general outline here: ;; 1. Do initial setup, and determine the heading between particle centers ;; (call it theta). ;; 2. Convert the representation of the velocity of each particle from ;; speed/heading to a theta-based vector whose first component is the ;; particle's speed along theta, and whose second component is the speed ;; perpendicular to theta. ;; 3. Modify the velocity vectors to reflect the effects of the collision. ;; This involves: ;; a. computing the velocity of the center of mass of the whole system ;; along direction theta ;; b. updating the along-theta components of the two velocity vectors. ;; 4. Convert from the theta-based vector representation of velocity back to ;; the usual speed/heading representation for each particle. ;; 5. Perform final cleanup and update derived quantities. to collide-with [ other-particle ] ;; particle procedure ;; local copies of other-particle's relevant quantities ;mass2 speed2 heading2 ;; quantities used in the collision itself ;theta ;; heading of vector from my center to the center of other-particle. ;v1t ;; velocity of self along direction theta ;v1l ;; velocity of self perpendicular to theta ;v2t v2l ;; velocity of other-particle, represented in the same way ;vcm ;; velocity of the center of mass of the colliding particles, ;; along direction theta ;;; PHASE 1: initial setup ;; for convenience, grab some quantities from other-particle let mass2 [mass] of other-particle let speed2 [speed] of other-particle let heading2 [heading] of other-particle ;; since particles are modeled as zero-size points, theta isn't meaningfully ;; defined. we can assign it randomly without affecting the model's outcome. let theta (random-float 360) ;;; PHASE 2: convert velocities to theta-based vector representation ;; now convert my velocity from speed/heading representation to components ;; along theta and perpendicular to theta let v1t (speed * cos (theta - heading)) let v1l (speed * sin (theta - heading)) ;; do the same for other-particle let v2t (speed2 * cos (theta - heading2)) let v2l (speed2 * sin (theta - heading2)) ;;; PHASE 3: manipulate vectors to implement collision ;; compute the velocity of the system's center of mass along theta let vcm (((mass * v1t) + (mass2 * v2t)) / (mass + mass2) ) ;; now compute the new velocity for each particle along direction theta. ;; velocity perpendicular to theta is unaffected by a collision along theta, ;; so the next two lines actually implement the collision itself, in the ;; sense that the effects of the collision are exactly the following changes ;; in particle velocity. set v1t (2 * vcm - v1t) set v2t (2 * vcm - v2t) ;;; PHASE 4: convert back to normal speed/heading ;; now convert my velocity vector into my new speed and heading set speed sqrt ((v1t * v1t) + (v1l * v1l)) ;; if the magnitude of the velocity vector is 0, atan is undefined. but ;; speed will be 0, so heading is irrelevant anyway. therefore, in that ;; case we'll just leave it unmodified. if v1l != 0 or v1t != 0 [ set heading (theta - (atan v1l v1t)) ] ;; and do the same for other-particle ask other-particle [ set speed sqrt ((v2t * v2t) + (v2l * v2l)) if v2l != 0 or v2t != 0 [ set heading (theta - (atan v2l v2t)) ] ] ;; PHASE 5: final updates ;; now recolor, since color is based on quantities that may have changed recolor ask other-particle [ recolor ] end to recolor ;; particle procedure ifelse speed < (0.5 * 10) [ set color blue ] [ ifelse speed > (1.5 * 10) [ set color red ] [ set color green ] ] end ;;; ;;; drawing procedures ;;; ;; draws the box to make-box ask patches with [ ((abs pxcor = box-edge) and (abs pycor <= box-edge)) or ((abs pycor = box-edge) and (abs pxcor <= box-edge)) ] [ set pcolor yellow ] ask patches with [pycor = 0 and pxcor < (1 - box-edge)] [ set pcolor yellow - 5 ;; trick the bounce code so particles don't go into the inlet ask patch-at 0 1 [ set pcolor yellow ] ask patch-at 0 -1 [ set pcolor yellow ] ] end ;; creates initial particles to make-particles [number] create-particles number [ setup-particle set speed random-float 20 random-position recolor ] calculate-tick-length end ;; adds particles from the left to add-particles-side if particles-to-add > 0 [ create-particles particles-to-add [ setup-particle setxy (- box-edge) 0 set heading 90 ;; east rt 45 - random-float 90 recolor ] if announce-add-particles? [ sound:play-note "tubular bells" 59 90 0.3 ] ;; announce when particles are added set particles-to-add 0 calculate-tick-length ] end ;; called by student from command center; ;; adds particles in middle to add-particles-middle [n] create-particles n [ setup-particle set new? true recolor ] ;; add the new particles to an agentset, so they ;; are accessible to the student from the command ;; center, e.g. "ask new-particles [ ... ]" set new-particles particles with [new?] calculate-tick-length end to setup-particle ;; particle procedure set new? false set speed 10 set mass 1.0 set last-collision nobody set wall-hits 0 set momentum-difference 0 end ;; place particle at random location inside the box. to random-position ;; particle procedure setxy ((1 - box-edge) + random-float ((2 * box-edge) - 2)) ((1 - box-edge) + random-float ((2 * box-edge) - 2)) end to-report last-n [n the-list] ifelse n >= length the-list [ report the-list ] [ report last-n n butfirst the-list ] end to fade-patches let trace-patches patches with [ shade-of? pcolor red or shade-of? pcolor blue or shade-of? pcolor green ] if any? trace-patches [ ask trace-patches [ set pcolor ( pcolor - 0.05 ) if (pcolor mod 10 < 1) [ set pcolor black ] ] ] end to sing-one-particle-speed ;; procedure for listening to one particle's speed - tone is a function of speed ifelse single-particle-speed? [ ifelse (three-speed > 3) [ sound:stop-note "oboe" (80 - 120 / ( three-speed )) sound:stop-note "oboe" 50 sound:start-note "oboe" (80 - 120 / ( [speed] of particle 3 )) single-particle-loudness ] [ sound:stop-note "oboe" (80 - 120 / ( three-speed )) sound:stop-note "oboe" 50 sound:start-note "oboe" 50 single-particle-loudness + 20 ] set three-speed [speed] of particle 3 ] [ ifelse (three-speed > 3) [ sound:stop-note "oboe" (80 - 120 / ( three-speed ))] [ sound:stop-note "oboe" 50 ] ] end to sing-real-time ;; real time is drummed at a regular interval every real-time-pacer [ sound:play-note "sci-fi" 30 60 0.3 ] end to sing-pressure ;; pressure is sung by a recorder with the tone a function of pressure if ( abs ( pressure-now - pressure ) > 0 and pressure-now != 0) [ sound:stop-note "recorder" ( 100 - 2000 / pressure-now ) sound:stop-note "recorder" ( 30 ) ] set pressure-now pressure ifelse (pressure > 30 ) [ sound:start-note "recorder" ( 100 - 2000 / pressure ) pressure-loudness ] [ sound:start-note "recorder" ( 30 ) pressure-loudness + 10 ] end ; Copyright 2004 Uri Wilensky. ; See Info tab for full copyright and license.
There are 10 versions of this model.
Attached files
File | Type | Description | Last updated | |
---|---|---|---|---|
GasLab With Sound.png | preview | Preview for 'GasLab With Sound' | over 11 years ago, by Uri Wilensky | Download |
This model does not have any ancestors.
This model does not have any descendants.