Child of Osmotic Pressure

No preview image

1 collaborator

55 Nathan Holbert (Author)

Tags

(This model has yet to be categorized with any tags)
Child of model Osmotic Pressure
Visible to everyone | Changeable by the author
Model was written in NetLogo 5.0.4 • Viewed 328 times • Downloaded 22 times • Run 0 times
Download the 'Child of Osmotic Pressure' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


Comments and Questions

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

globals [
  left-side                                 ;; left side of the membrane
  right-side                                ;; right side of the membrane
  split                                     ;; location of membrane
  pre-split                                 ;; a value for calculating the change in membrane location
  equilibrium                               ;; the difference in number of particles moving each direction across membrane
  tick-delta                                ;; how much we advance the tick counter this time through
  collisions        ;; list used to keep track of future collisions
  particle1         ;; first particle currently colliding
  particle2         ;; second particle currently colliding
]

breed [particles particle]
breed [membranes membrane]

turtles-own [
  speed
  mass
  energy
  old-pos
  new-pos
  part-type
]

to setup
  ;; (for this model to work with NetLogo's new plotting features,
  ;; __clear-all-and-reset-ticks should be replaced with clear-all at
  ;; the beginning of your setup procedure and reset-ticks at the end
  ;; of the procedure.)
  clear-all
  reset-ticks
  set-default-shape particles "circle"
  create-container
  spawn-particles
  set split 0
  calculate-wall
  set particle1 nobody
  set particle2 nobody
  set collisions []
  ask particles [ check-for-wall-collision ]
  ask particles [ check-for-particle-collision ]
  my-update-plots
end 

to create-container
  ask patches with [pycor = max-pycor or pycor = min-pycor] [              ;; sets the walls of the container
    set pcolor blue
  ]
  ask patches with [pxcor = max-pxcor or pxcor = min-pxcor] [
    set pcolor blue
  ]
  set left-side patches with [pxcor < 0]                                   ;; defines the left and right sides of the container equally
  set right-side patches with [pxcor > 0]
  set equilibrium 0                                                        ;; sets equilibrium to starting value (the middle of the container)
  set split 0                                                              ;; sets the starting location of the membrane
  ask patches with [pxcor = 0 and pycor != max-pycor and pycor != min-pycor and abs pycor mod 2 != 0] [
    sprout-membranes 1 [                                                   ;; create the membrane
      set shape "square"
      set color red
      set size 1.3
      set heading 90
    ]
  ]
end 

to setup-particles                                                      ;; set variable values for each new particle
  set speed 0.5
  if part-type = "solvent" [
    set size 1
  ]
  if part-type = "solute" [
    set size 2
  ]
  set mass (size * size)
  set energy (0.5 * mass * speed * speed)
end 

to-report overlapping?  ;; particle procedure
  ;; here, we use IN-RADIUS just for improved speed; the real testing
  ;; is done by DISTANCE
  report any? other turtles in-radius ((size + 2) / 2)
                              with [distance myself < (size + [size] of myself) / 2]
end 

to-report wall-lapping?  ;; particle procedure
  report any? patches in-radius ((size + 2) / 2) with [pcolor = blue] or
  any? membranes in-radius ((size + 2) / 2)
end 

to start-loc
  move-to one-of patches with [pcolor = black]                        ;; randomly distribute them throughout the world
    while [overlapping?] [
      move-to one-of patches with [pcolor = black]
    ]
    while [wall-lapping?] [
      move-to one-of patches with [pcolor = black]
    ]
end 

to start-right-loc
  move-to one-of right-side with [pcolor = black]                        ;; randomly distribute them throughout the world
    while [overlapping?] [
      move-to one-of right-side with [pcolor = black]
    ]
    while [wall-lapping?] [
      move-to one-of right-side with [pcolor = black]
    ]
end 

to start-left-loc
  move-to one-of left-side with [pcolor = black]                        ;; randomly distribute them throughout the world
    while [overlapping?] [
      move-to one-of left-side with [pcolor = black]
    ]
    while [wall-lapping?] [
      move-to one-of left-side with [pcolor = black]
    ]
end 

to spawn-particles
  if solute = "Sugar" [                                                ;; checks to see the identity of the solute based on the chooser
    output-type "C12H22O11"                                            ;; write the chemical formula in the output area
    create-particles solute-left [                                     ;; create solute particles based on slider value
      set color white
      set shape "circle"
      set part-type "solute"                                        ;; define these as solute particles
      setup-particles
      start-left-loc
    ]
    create-particles solute-right [                                    ;; create solute particles based on slider value
      set color white
      set shape "circle"
      set part-type "solute"
      setup-particles
      start-right-loc
    ]
  ]

  if solute = "Sodium Chloride" [
    output-type "NaCl"
    create-particles solute-left [
      set color white
      set shape "circle"
      set part-type "solute"
      setup-particles
      start-left-loc
      ask patch-here [
        sprout-particles 1 [                                   ;; solutes that dissociate in water split into their respective ions
          set color white                                      ;; since NaCl splits into two ions, one new turtle is sprouted for each solute molecule
          set shape "circle"
          set part-type "solute"
          setup-particles
          fd 0.2
          ]
      ]
    ]
    create-particles solute-right [
      set color white
      set shape "circle"
      set part-type "solute"
      setup-particles
      start-right-loc
      ask patch-here [
        sprout-particles 1 [
          set color white
          set shape "circle"
          set part-type "solute"
          setup-particles
          fd 0.2
          ]
      ]
    ]
  ]

  if solute = "Magnesium Chloride" [
    output-type "MgCl2"
    create-particles solute-left [
      set color white
      set shape "circle"
      set part-type "solute"
      setup-particles
      start-left-loc
      ask patch-here [
        sprout-particles 2 [                                        ;; splits into 3 ions..so 2 new turtles are sprouted
          set color white
          set shape "circle"
          set part-type "solute"
          setup-particles
          fd 0.2
          ]
      ]
    ]
    create-particles solute-right [
      set color white
      set shape "circle"
      set part-type "solute"
      setup-particles
      start-right-loc
      ask patch-here [
        sprout-particles 2 [
          set color white
          set shape "circle"
          set part-type "solute"
          setup-particles
          fd 0.2
          ]
      ]
    ]
  ]

  if solute = "Aluminum Chloride" [
    output-type "AlCl3"
    create-particles solute-left [
      set color white
      set shape "circle"
      set part-type "solute"
      setup-particles
      start-left-loc
      ask patch-here [
        sprout-particles 3 [                                     ;; splits into 4 ions...so 3 new turtles are sprouted
          set color white
          set shape "circle"
          set part-type "solute"
          setup-particles
          fd 0.2
          ]
      ]
    ]
    create-particles solute-right [
      set color white
      set shape "circle"
      set part-type "solute"
      setup-particles
      start-right-loc
      ask patch-here [
        sprout-particles 3 [
          set color white
          set shape "circle"
          set part-type "solute"
          setup-particles
          fd 0.2
          ]
      ]
    ]
  ]
  create-particles (100 - count particles with [part-type = "solute" and xcor < split]) [                                               ;; creates 1000 water molecules
    set color blue + 2
    set part-type "solvent"                                             ;; define these as solvents
    setup-particles
    start-left-loc
  ]
  create-particles (100 - count particles with [part-type = "solute" and xcor > split]) [                                               ;; creates 1000 water molecules
    set color blue + 2
    set part-type "solvent"                                             ;; define these as solvents
    setup-particles
    start-right-loc
  ]
end 

to particle-jump
  ask particles with [xcor >= pre-split and xcor <= split and part-type = "solutes"] [     ;; check for solutes in the way of a membrane jump
    set xcor (split - pre-split) + 1
;    recalculate-particles-that-just-collided
  ]
  ask particles with [xcor <= pre-split and xcor >= split and part-type = "solutes"] [
    set xcor (pre-split - split) + 1
;    recalculate-particles-that-just-collided
  ]
end 

to calculate-wall
  set pre-split split                                                  ;; save location of the membrane before it moves
  let nudge ((equilibrium * -1) / 10)                                  ;; calculates the amount to move the membrane
  set split split + nudge                                              ;; set split to be the new location of the membrane
  particle-jump                                                        ;; move solutes in the way of the membrane jump
  ask membranes [
    set xcor xcor + nudge                                              ;; move membrane turtles according to nudge value
  ]
  
  set left-side patches with [pxcor < split]                           ;; redefine right and left sides
  set right-side patches with [pxcor > split]
  set equilibrium 0                               ;; reset equilibrium so we can keep track of what happens during the next tick
end 

to go
  if count turtles-on right-side = 0 or count turtles-on left-side = 0 [                    ;; stops model if membrane reaches either edge
    user-message "The membrane has burst! Make sure you have some solute on both sides!"
    stop
  ]
  ask particles [ set old-pos xcor ]
  choose-next-collision

  ask particles with [ part-type = "solute" ] [      ;; bouncing for solutes hitting membrane
    if any? membranes in-cone 2 180 [
      set heading (- heading)
    ]
  ]

  ask particles [ jump speed * tick-delta ]
  perform-next-collision

  ask particles with [ part-type = "solvent" ] [
    if old-pos < split and xcor >= split [                      ;; if a solvent moves from the left of the membrane to the right of the membrane
      set equilibrium equilibrium + 1                           ;; add one to equilibrium
    ]
    if old-pos > split and xcor <= split [                      ;; if a solvent moves from the right of the membrane to the left of the membrane
      set equilibrium equilibrium - 1                           ;; subract one from equilibrium
    ]
  ]

  calculate-wall                                                ;; recalculate the new location of the wall
  tick-advance tick-delta
  recalculate-particles-that-just-collided
  my-update-plots
end 

to recalculate-particles-that-just-collided
  ;; Since only collisions involving the particles that collided most recently could be affected,
  ;; we filter those out of collisions.  Then we recalculate all possible collisions for
  ;; the particles that collided last.  The ifelse statement is necessary because
  ;; particle2 can be either a particle or a string representing a wall.  If it is a
  ;; wall, we don't want to invalidate all collisions involving that wall (because the wall's
  ;; position wasn't affected, those collisions are still valid.
  ifelse is-turtle? particle2
    [
      set collisions filter [item 1 ? != particle1 and
                             item 2 ? != particle1 and
                             item 1 ? != particle2 and
                             item 2 ? != particle2]
                            collisions
      ask particle2 [ check-for-wall-collision ]
      ask particle2 [ check-for-particle-collision ]
    ]
    [
      set collisions filter [item 1 ? != particle1 and
                             item 2 ? != particle1]
                            collisions
    ]
  if particle1 != nobody [ ask particle1 [ check-for-wall-collision ] ]
  if particle1 != nobody [ ask particle1 [ check-for-particle-collision ] ]
  ;; Slight errors in floating point math can cause a collision that just
  ;; happened to be calculated as happening again a very tiny amount of
  ;; time into the future, so we remove any collisions that involves
  ;; the same two particles (or particle and wall) as last time.
  set collisions filter [item 1 ? != particle1 or
                         item 2 ? != particle2]
                        collisions
  ;; All done.
  set particle1 nobody
  set particle2 nobody
end 

;; check-for-particle-collision is a particle procedure that determines the time it takes
;; to the collision between two particles (if one exists).  It solves for the time by representing
;; the equations of motion for distance, velocity, and time in a quadratic equation of the vector
;; components of the relative velocities and changes in position between the two particles and
;; solves for the time until the next collision

to check-for-particle-collision
  let my-x xcor
  let my-y ycor
  let my-particle-size size
  let my-x-speed speed * sin heading
  let my-y-speed speed * cos heading
  ask other particles
  [
    let dpx (xcor - my-x)   ;; relative distance between particles in the x direction
    let dpy (ycor - my-y)    ;; relative distance between particles in the y direction
    let x-speed (speed * sin heading) ;; speed of other particle in the x direction
    let y-speed (speed * cos heading) ;; speed of other particle in the x direction
    let dvx (x-speed - my-x-speed) ;; relative speed difference between particles in x direction
    let dvy (y-speed - my-y-speed) ;; relative speed difference between particles in y direction
    let sum-r (((my-particle-size) / 2 ) + (([size] of self) / 2 )) ;; sum of both particle radii

    ;; To figure out what the difference in position (P1) between two particles at a future
    ;; time (t) will be, one would need to know the current difference in position (P0) between the
    ;; two particles and the current difference in the velocity (V0) between the two particles.
    ;;
    ;; The equation that represents the relationship is:
    ;;   P1 = P0 + t * V0
    ;; we want find when in time (t), P1 would be equal to the sum of both the particle's radii
    ;; (sum-r).  When P1 is equal to is equal to sum-r, the particles will just be touching each
    ;; other at their edges (a single point of contact).
    ;;
    ;; Therefore we are looking for when:   sum-r =  P0 + t * V0
    ;;
    ;; This equation is not a simple linear equation, since P0 and V0 should both have x and y
    ;; components in their two dimensional vector representation (calculated as dpx, dpy, and
    ;; dvx, dvy).
    ;;
    ;; By squaring both sides of the equation, we get:
    ;;   (sum-r) * (sum-r) =  (P0 + t * V0) * (P0 + t * V0)
    ;; When expanded gives:
    ;;   (sum-r ^ 2) = (P0 ^ 2) + (t * PO * V0) + (t * PO * V0) + (t ^ 2 * VO ^ 2)
    ;; Which can be simplified to:
    ;;   0 = (P0 ^ 2) - (sum-r ^ 2) + (2 * PO * V0) * t + (VO ^ 2) * t ^ 2
    ;; Below, we will let p-squared represent:   (P0 ^ 2) - (sum-r ^ 2)
    ;; and pv represent: (2 * PO * V0)
    ;; and v-squared represent: (VO ^ 2)
    ;;
    ;;  then the equation will simplify to:     0 = p-squared + pv * t + v-squared * t^2

    let p-squared   ((dpx * dpx) + (dpy * dpy)) - (sum-r ^ 2)   ;; p-squared represents difference
    ;; of the square of the radii and the square of the initial positions

    let pv  (2 * ((dpx * dvx) + (dpy * dvy)))  ;; vector product of the position times the velocity
    let v-squared  ((dvx * dvx) + (dvy * dvy)) ;; the square of the difference in speeds
    ;; represented as the sum of the squares of the x-component
    ;; and y-component of relative speeds between the two particles

    ;; p-squared, pv, and v-squared are coefficients in the quadratic equation shown above that
    ;; represents how distance between the particles and relative velocity are related to the time,
    ;; t, at which they will next collide (or when their edges will just be touching)

    ;; Any quadratic equation that is a function of time (t) can be represented as:
    ;;   a*t*t + b*t + c = 0,
    ;; where a, b, and c are the coefficients of the three different terms, and has solutions for t
    ;; that can be found by using the quadratic formula.  The quadratic formula states that if a is
    ;; not 0, then there are two solutions for t, either real or complex.
    ;; t is equal to (b +/- sqrt (b^2 - 4*a*c)) / 2*a
    ;; the portion of this equation that is under a square root is referred to here
    ;; as the determinant, D1.   D1 is equal to (b^2 - 4*a*c)
    ;; and:   a = v-squared, b = pv, and c = p-squared.
    let D1 pv ^ 2 -  (4 * v-squared * p-squared)

    ;; the next test tells us that a collision will happen in the future if
    ;; the determinant, D1 is > 0,  since a positive determinant tells us that there is a
    ;; real solution for the quadratic equation.  Quadratic equations can have solutions
    ;; that are not real (they are square roots of negative numbers).  These are referred
    ;; to as imaginary numbers and for many real world systems that the equations represent
    ;; are not real world states the system can actually end up in.

    ;; Once we determine that a real solution exists, we want to take only one of the two
    ;; possible solutions to the quadratic equation, namely the smaller of the two the solutions:
    ;;  (b - sqrt (b^2 - 4*a*c)) / 2*a
    ;;  which is a solution that represents when the particles first touching on their edges.
    ;;  instead of (b + sqrt (b^2 - 4*a*c)) / 2*a
    ;;  which is a solution that represents a time after the particles have penetrated
    ;;  and are coming back out of each other and when they are just touching on their edges.

    let time-to-collision  -1

    if D1 > 0
      [ set time-to-collision (- pv - sqrt D1) / (2 * v-squared) ]        ;; solution for time step

    ;; if time-to-collision is still -1 there is no collision in the future - no valid solution
    ;; note:  negative values for time-to-collision represent where particles would collide
    ;; if allowed to move backward in time.
    ;; if time-to-collision is greater than 1, then we continue to advance the motion
    ;; of the particles along their current trajectories.  They do not collide yet.

    if time-to-collision > 0
    [
      ;; time-to-collision is relative (ie, a collision will occur one second from now)
      ;; We need to store the absolute time (ie, a collision will occur at time 48.5 seconds.
      ;; So, we add clock to time-to-collision when we store it.
      ;; The entry we add is a three element list of the time to collision and the colliding pair.
      set collisions fput (list (time-to-collision + ticks) self myself)
                          collisions
    ]
  ]
end 


;; determines when a particle will hit any of the four walls or membrane

to check-for-wall-collision  ;; particle procedure
  ;; right & left walls
  let x-speed (speed * sin heading)
  if x-speed != 0
    [ ;; solve for how long it will take particle to reach right wall
      let right-interval (max-pxcor - 0.5 - xcor - size / 2) / x-speed
      if right-interval > 0
        [ assign-colliding-wall right-interval "right wall" ]
      ;; solve for time it will take particle to reach left wall
      let left-interval ((- max-pxcor) + 0.5 - xcor + size / 2) / x-speed
      if left-interval > 0
        [ assign-colliding-wall left-interval "left wall" ] ]

;  ;; membrane
;  if x-speed != 0 and part-type = "solute" and xcor < split
;    [ ;; solve for how long it will take particle to reach the membrane from the left
;      let left-interval2 (split - xcor - size / 2) / x-speed
;      if left-interval2 > 0
;        [ assign-colliding-wall left-interval2 "membrane-left" ] ]
;   if x-speed != 0 and part-type = "solute" and xcor < split
;    [ ;; solve for time it will take particle to reach the membrane from the right
;      let right-interval2 (split - xcor + size / 2) / x-speed
;      if right-interval2 > 0
;        [ assign-colliding-wall right-interval2 "membrane-right" ] ]

  ;; top & bottom walls
  let y-speed (speed * cos heading)
  if y-speed != 0
    [ ;; solve for time it will take particle to reach top wall
      let top-interval (max-pycor - 0.5 - ycor - size / 2) / y-speed
      if top-interval > 0
        [ assign-colliding-wall top-interval "top wall" ]
      ;; solve for time it will take particle to reach bottom wall
      let bottom-interval ((- max-pycor) + 0.5 - ycor + size / 2) / y-speed
      if bottom-interval > 0
        [ assign-colliding-wall bottom-interval "bottom wall" ] ]
end 

to assign-colliding-wall [time-to-collision wall]  ;; particle procedure
  ;; this procedure is used by the check-for-wall-collision procedure
  ;; to assemble the correct particle-wall pair
  ;; time-to-collision is relative (ie, a collision will occur one second from now)
  ;; We need to store the absolute time (ie, a collision will occur at time 48.5 seconds.
  ;; So, we add clock to time-to-collision when we store it.
  let colliding-pair (list (time-to-collision + ticks) self wall)
  set collisions fput colliding-pair collisions
end 

to choose-next-collision
  if collisions = [] [ stop ]
  ;; Sort the list of projected collisions between all the particles into an ordered list.
  ;; Take the smallest time-step from the list (which represents the next collision that will
  ;; happen in time).  Use this time step as the tick-delta for all the particles to move through
  let winner first collisions
  foreach collisions [ if first ? < first winner [ set winner ? ] ]
  ;; winner is now the collision that will occur next
  let dt item 0 winner
  ;; If the next collision is more than 1 in the future,
  ;; only advance the simulation one tick, for smoother animation.
  set tick-delta dt - ticks
  if tick-delta > 1
    [ set tick-delta 1
      set particle1 nobody
      set particle2 nobody
      stop ]
  set particle1 item 1 winner
  set particle2 item 2 winner
end 

to perform-next-collision
  ;; deal with 3 possible cases:
  ;; 1) no collision at all
  if particle1 = nobody [ stop ]
  ;; 2) particle meets wall
  if is-string? particle2
    [ if particle2 = "left wall" or particle2 = "right wall" ;or particle2 = "membrane-left" or particle2 = "membrane-right"
        [ ask particle1 [ set heading (- heading) ]
          stop ]
      if particle2 = "top wall" or particle2 = "bottom wall"
        [ ask particle1 [ set heading 180 - heading ]
          stop ] ]
  ;; 3) particle meets particle
  ask particle1 [ collide-with particle2 ]
end 

to collide-with [other-particle]  ;; particle procedure
  ;;; 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
  ;; modified so that theta is heading toward other particle
  let theta towards other-particle

  ;;; 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.
  set energy (0.5 * mass * speed ^ 2)

  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 ^ 2) + (v2l ^ 2))
    set energy (0.5 * mass * speed ^ 2)

    if v2l != 0 or v2t != 0
      [ set heading (theta - (atan v2l v2t)) ]
  ]
end 

to my-update-plots
  set-current-plot "Water#"
  set-current-plot-pen "left"
  plot count particles with [pxcor < split and part-type = "solvent"]
  set-current-plot-pen "right"
  plot count particles with [pxcor > split and part-type = "solvent"]
end 

There is only one version of this model, created about 11 years ago by Nathan Holbert.

Attached files

No files

Parent: Osmotic Pressure

This model does not have any descendants.

Graph of models related to 'Child of Osmotic Pressure'