breed [inerts inert]
breed [reactants reactant]
breed [products product]
turtles-own [conv-xcor conv-ycor diff-xcor diff-ycor res-time]
globals [effluent effluent-reactant effluent-product effluent-rtd-list effluent-rtd reactant-profile reactant-profile-patches]


;;GLOBAL VARIABLES:
  ;;effluent = agentset of turtles leaving the right system boundary at each time point
  ;;effluent-reactant = effluent with reactant breed
  ;;effluent-product = effluent with product breed
  ;;effluent-rtd-list = list of lists containing the residence times of all agents in the effluent. At each tick, a new list for the current effluent is appended to the end, and the first list is removed if the sampling window is exceeded.
  ;;effluent-rtd = single list of residence times of all effluent within the sampling window
  ;;reactant-profile = list of total reactant count in the current model for each value of pxcor, ordered by pxcor
  ;;reactant-profile-patches = list of total reactant count imported from the patch-based model for each value of pxcor, ordered by pxcor


;;TURTLE VARIABLES:
  ;;conv-xcor/conv-ycor = the projected x-y coordinates of each turtle after convection
  ;;diff-xcor/diff-ycor = the projected x-y coordinates of each turtle after convection and dispersion
  ;;res-time = the residence time of an individual turtle, incremented at each tick
  


to setup
  clear-all
    
  ;;Set default shapes
  set-default-shape turtles "circle"
  
  ;;Initialize sets and lists
  set reactant-profile ([pycor] of patches with [pycor = 0])  ;;Create an array of zeroes with length equal to reactor length
  set effluent (turtles with [xcor = max-pxcor])
  set effluent-reactant 0
  set effluent-product 0
  set effluent-rtd-list []
  set effluent-rtd []
  
  ;;Fill reactor 
  ifelse fill-reactor? 
    [fill-reactor]
    [ask patches with [pxcor = min-pxcor] [feed-reactor]]
  
  reset-ticks  
  
end


to fill-reactor
;;Fills entire reactor with reactant at setup.  This avoids time delay due to convection, but still incurs delay due to residence time.

  ;;Ask patches with xcor equal to multiple of flow discretization to sprout reactant
  ask patches with [pxcor = feed-velocity * floor (pxcor / feed-velocity)]
    [sprout-reactants feed-concentration
      [set color blue
        set size 1
        set heading 90
        set res-time 0]]
end



to go
;;Note that for units, 1 tick = 1 second
  
  run-kinetics
  convective-transport
  dispersive-transport
  analyze-effluent
  update-plots
  move
  ask patches with [pxcor = min-pxcor] [feed-reactor]
  calculate-profile
  tick

end



to feed-reactor  ;;Patch procedure
;;ADDS MOLECULAR PACKETS AT REACTOR INLET  
  
  ;;Add reactant to feed
  sprout-reactants feed-concentration
  [set color blue
    set size 1
    set heading 90
    set res-time 0]
  
  
  ;;Add tracer during step experiments
  if tracer-step? 
    [sprout-inerts tracer-density
      [set color yellow
        set size 2
        set heading 90
        set res-time 0]]

end




to tracer-pulse
  ;;Add single pulse of tracer to feed
  
  ask patches with [pxcor = min-pxcor]
    [sprout-inerts tracer-density
      [set color yellow
        set size 2
        set heading 90
        set res-time 0]]

  
end



to run-kinetics
;;RUNS REACTION KINETICS ACCORDING TO SELECTED MECHANISM


  ;;Conc. Independent: Reaction proceeds with equal probability regardless of local concentration
  if reaction-type = "Concentration Independent"
    [ask reactants 
      [if random-float 100 < reaction-probability
        [set breed products
         set color red]]]


  ;;Conc. Dependent:  Reaction probability scales proportionally with the local reactant concentration.
  ;;Reactant concentration is based upon reactants within the collision radius.
   if reaction-type = "Concentration Dependent"
    [ask reactants 
      [if random-float 100 < (reaction-probability * ( count (other reactants in-radius collision-radius) ))
        [set breed products
         set color red]]]

 
  ;;Bimolecular.  Reaction does not proceed unless another reactant is within the collision radius.
   if reaction-type = "Bimolecular"
      ;;Ask reactants to do: I check if I am within the collision-radius of another reactant, if so, 
      ;;and we probabilistically have the correct orientation, we react.  
      ;;When we react, I ask the other reactant to die, then I become the product.
 
    [ask reactants 
      [if (count (other reactants in-radius collision-radius) > 0) and random-float 100 < reaction-probability  
        [ask one-of other reactants in-radius collision-radius 
          [die]
         set breed products
         set color red]]]

end



to convective-transport
;;BULK FLOW TRANSPORT MECHANISM
    
  ;;Ask turtles to predict their downstream position
  ask turtles [
    set heading 90
    set conv-xcor (xcor + feed-velocity) ]

end



to dispersive-transport
;;DISPERSIVE TRANSPORT MECHANISM
;;Ask turtles to predict their post-diffusion position relative to their predicted post-convection position.
  
  ;;Tell all turtles: I take a random walk of path-length equal to "diffusivity." 
  ask turtles [ 
    
    ;;If random diffusion is on, the random walk step size is a random float less than the diffusivity.  This helps smooth the data, but isn't a true fixed-step random walk.
    ifelse not random-diffusion?
      [ifelse random 2 = 1
        [set diff-xcor (conv-xcor + (diffusivity))]
        [set diff-xcor (conv-xcor - (diffusivity))]]
    
      [ifelse random 2 = 1
        [set diff-xcor (conv-xcor + (random-float diffusivity))]
        [set diff-xcor (conv-xcor - (random-float diffusivity))]]
    
    
    if diff-xcor < min-pxcor
      [set diff-xcor min-pxcor]]
       
  
end




to analyze-effluent
;;BOOKKEEPING OF ELEMENTS LEAVING THE REACTOR
  
  
  ;;IDENTIFY PARTICLES LEAVING THE REACTOR
  
  ;;Store all molecules leaving the reactor as effluent
  set effluent (turtles with [diff-xcor > max-pxcor])
 
  ;;Store effluent reactant (moving average does not store past values to save memory)
  set effluent-reactant (((sampling-window - 1) / sampling-window) * effluent-reactant) + ((1 / sampling-window) * (count effluent with [breed = reactants]) )

  ;;Store effluent product (moving average does not store past values to save memory)
  set effluent-product (((sampling-window - 1) / sampling-window) * effluent-product) + ((1 / sampling-window) * (count effluent with [breed = products]) )
   
   
  ;;STORE EFFLUENT RESIDENCE TIMES
  
  ;;Add effluent residence times to sliding window. effluent-rtd-list is a list of sets of residence times for the effluent molecules at each tick.
  set effluent-rtd-list lput ([res-time] of effluent) effluent-rtd-list
  
  ;;Removes the oldest set of residence times.
  if (length effluent-rtd-list) > sampling-window
    [set effluent-rtd-list remove-item 0 effluent-rtd-list]
    
  ;;Combines residence time lists into a single list for plotting.
  set effluent-rtd effluent-rtd-merged
 
end



to move
;;EXECUTES ACTUAL MOTION

  ;;Remove effluent from reactor
  ask effluent [die]
  
  ;;Turtles reach their projected position after convective + diffusive transport, then increment residence time
  ask turtles [
    set xcor diff-xcor
    set res-time (res-time + 1)]
     
end



to calculate-profile
  
  ;;Ask each patch along the longitudinal axis of the reactor to store the current total number of reactants with the same x-coordinate
  ;;This value is then appended to the moving average reactant-profile across the sampling window

  let centerline (patches with [pycor = 0])
  let inst-reactant-profile (map ([sum ([count reactants-here] of patches with [pxcor = [pxcor] of ?])]) (sort-on [pxcor] centerline))
  set reactant-profile (  map [((1 / sampling-window) * ?1) + (((sampling-window - 1) / sampling-window) * ?2)] inst-reactant-profile reactant-profile)

end




;;End of main model code.  Remaining code is for analysis and import/export.
;;===================================================================================


;;IMPORT EXPORT CODE FOR SYNCING WITH PATCH-BASED MODEL


to import-patch-model
;;FUNCTION IMPORTS PARAMETERS AND RESULTS FROM PATCH-BASED MODEL
;;THEN SETS CURRENT MODEL TO THOSE OPERATING PARAMETERS 
  
  ;;Open file containing data from turtle-based model
  file-open "patch-results.csv"
  
  ;;If file is at the end (i.e. already imported once), close it and reopen it.
  if file-at-end? 
    [file-close-all
      file-open "patch-results.csv"]
  
  ;;Store each of the variables from the other model.  List order is known a priori, not ideal but... no tengo tiempo!
  let dim                 read-from-string (file-read-line)
  resize-world (item 0 dim) (item 1 dim) (item 2 dim) (item 3 dim)   ;;Set dimensions to those of patch model
  set feed-velocity       read-from-string (file-read-line)          ;;Sets feed-velocity to patch-based model's value
  set diffusivity         round (read-from-string (file-read-line))  ;;Sets diffusivity to patch-based model's value.
  set reaction-probability (100 * read-from-string (file-read-line)) ;;Sets reaction-probability to patch-based model's rate-constant
  set reactant-profile-patches read-from-string (file-read-line)          ;;Import results (conc. profile) from patch-based model
  
                                                                          ;;Close file, resetting line count.
  file-close-all

end



to export-results
;;WRITE OPERATING PARAMETERS AND CONCENTRATION PROFILE TO A FILE FROM WHICH IT CAN BE IMPORTED TO PATCH MODEL

  ;;Normalize reactant concentration profile to stable concentration near end of reactor. I've shifted this right because the bounds on random walk cause an anomaly near the inlet.
  let reactant-profile-norm (map [? / (item (world-width - 10) reactant-profile)] reactant-profile )
  
  ;;Create file (or delete old one and reopen)   
  ifelse file-exists? "turtle-results.csv"
    [file-delete "turtle-results.csv"
      file-open "turtle-results.csv" ]
    [file-open "turtle-results.csv"]
  
  ;;Write variables to file.  I've left out labels because I can't figure out how to make the import step recognize them.
  file-print (list min-pxcor max-pxcor min-pycor max-pycor)
  file-print feed-velocity
  file-print diffusivity
  file-print (reaction-probability / 100)  ;;Converted to percentage probability
  file-print reaction-type
  file-print reactant-profile-norm     
  
  ;;Save and close file  
  file-flush
  file-close


end




;; REPORTERS - ONLY USED FOR ANALYSIS


to-report space-time
;;CALCULATE SPACE TIME (convection/reactor volume)
 
  ifelse feed-velocity > 0
    [report (((2 * max-pxcor) + 1) / feed-velocity)]
    [report "inf"]
  
end


to-report conversion
;;CALCULATE CONVERSION  

  let reactant-in (count reactants with [res-time = 0])
  report (( reactant-in - effluent-reactant ) / reactant-in)

end


to-report peclet []
;;CALCULATE PECLET NUMBER (dimensionless ratio = convection/dispersion)
 
  if length effluent-rtd > 1 
    [ifelse non-dim-res-time?
      [if length effluent-rtd > 1
        [ifelse variance effluent-rtd > 0 
          [report (2 / (variance effluent-rtd))]
          [report "inf."] ]]
      [ifelse variance effluent-rtd != 0
        [report ((2 *  (space-time ^ 2)) / (variance effluent-rtd)) ]
        [report "inf."]]]
  
end


to-report effluent-rtd-merged
  
;;CALCULATE RESIDENCE TIME DISTRIBUTION   
 
  ;;Takes list of lists and combines them into a single list of residence times for the given sliding window
  let rtd-list reduce [sentence ?1 ?2] effluent-rtd-list
  
  ;;Nondimensionalize residence times
  ifelse non-dim-res-time? 
    [report map [ ? / (space-time)] rtd-list]
    [report rtd-list]

end


to-report mean-sq-error [profile1 profile2]
  
  ;;Rescales patch profile based on concentration ten patches from the reactor exit
  let scaled-profile2 (map [? *  (item (world-width - 10) profile1)] profile2)
  
  ;;Filters out first ten and last five entries from profile.  These are the regions where noise has a strong influence.
  let filtered-profile1 sublist profile1 (10) ((length profile1) - 5)
  let filtered-profile2 sublist scaled-profile2 (10) ((length scaled-profile2) - 5)
  
  ;;Calculates sum of mean squared error between profiles
  let squared-errors (map [(?1 - ?2) ^ 2] filtered-profile1 filtered-profile2)

  ;;Report sum of squared error
  report (sum squared-errors)   

end


@#$#@#$#@
GRAPHICS-WINDOW
373
65
686
219
50
20
3.0
1
10
1
1
1
0
0
0
1
-50
50
-20
20
0
0
1
seconds
30.0

BUTTON
42
16
124
50
NIL
setup
NIL
1
T
OBSERVER
NIL
NIL
NIL
NIL
1

BUTTON
162
69
236
103
NIL
go
T
1
T
OBSERVER
NIL
NIL
NIL
NIL
1

BUTTON
56
69
158
103
single-step
go
NIL
1
T
OBSERVER
NIL
NIL
NIL
NIL
1

TEXTBOX
-149
46
18
66
SETUP
11
0.0
1

SLIDER
70
215
241
248
feed-velocity
feed-velocity
0
10
1
1
1
steps/tick
HORIZONTAL

SLIDER
64
389
242
422
reaction-probability
reaction-probability
0
3
3
0.01
1
NIL
HORIZONTAL

PLOT
485
436
668
586
Conversion
Time
Conversion
0.0
1.0
0.0
1.0
true
false
"" "ifelse ticks < (10 * sampling-window)\n  [set-plot-x-range 0 (ticks + 1)]\n  [set-plot-x-range (ticks - (10 * sampling-window)) (ticks + 1)]\n\nset-plot-y-range 0 1"
PENS
"Conversion" 1.0 0 -16777216 true "" "plot conversion"

SLIDER
314
392
464
425
sampling-window
sampling-window
1
200
10
1
1
NIL
HORIZONTAL

MONITOR
529
389
632
434
Conversion
conversion
2
1
11

SLIDER
74
286
244
319
diffusivity
diffusivity
0
50
6
0.1
1
NIL
HORIZONTAL

CHOOSER
42
463
260
508
reaction-type
reaction-type
"Concentration Independent" "Concentration Dependent" "Bimolecular"
0

BUTTON
1142
354
1248
388
Tracer Pulse
tracer-pulse\n
NIL
1
T
OBSERVER
NIL
NIL
NIL
NIL
1

PLOT
1084
436
1284
586
Effluent Tracer
Time
Tracer Conc.
0.0
10.0
0.0
10.0
true
false
"" "if tracer-step?\n  [set-plot-y-range 0 (tracer-density * 3 * max-pycor)]\n\nset-plot-x-range (floor (ticks - (space-time * 5))) (1 + ticks)\nplotxy (ticks) (count effluent with [breed = inerts])"
PENS
"Tracer Conc." 1.0 0 -16777216 true "" ""

SLIDER
46
138
272
171
feed-concentration
feed-concentration
1
3
1
1
1
mol/patch
HORIZONTAL

SWITCH
129
16
258
49
fill-reactor?
fill-reactor?
1
1
-1000

PLOT
876
436
1076
586
Residence Time Distribution
Residence Time
Bin Count
0.0
10.0
0.0
10.0
true
false
"" "if not empty? effluent-rtd and ticks > 0 and (max effluent-rtd) != 0\n  [set-plot-x-range 0 (ceiling (1.5 * max effluent-rtd))\n   set-histogram-num-bars 500\n   set-plot-pen-mode 0]"
PENS
"default" 1.0 0 -16777216 true "" "histogram effluent-rtd"

SLIDER
1123
395
1260
428
tracer-density
tracer-density
1
50
10
1
1
NIL
HORIZONTAL

SWITCH
1129
314
1262
347
tracer-step?
tracer-step?
1
1
-1000

PLOT
673
436
873
586
Concentration Profile
Reactor Length
Concentration
0.0
10.0
0.0
10.0
false
true
"" "clear-plot\n   set-plot-x-range (min-pxcor) max-pxcor\n   set-plot-y-range 0 ( max-pxcor * feed-concentration)\n  \n  set-current-plot-pen \"Turtle\"\n  ;;Store x-positions in order, then plot x-y pairs between lists.  \n  let centerline (patches with [pycor = 0])\n  let positions (sort ([pxcor] of centerline))\n  (foreach positions reactant-profile \n    [if ?2 > (0.05 * max reactant-profile) [plotxy ?1 ?2]]  )  \n  \n  ;;If the imported patch-based profile exists, plot it in red.\n  if reactant-profile-patches != 0\n    [set-current-plot-pen \"Patch\"\n    \n    ;;Rescale concentrations based on 10th patch from end of reactor\n    let patch-profile (map [? *  (item (world-width - 10) reactant-profile)] reactant-profile-patches)\n    (foreach positions patch-profile [plotxy ?1 ?2])]"
PENS
"Turtle" 1.0 0 -16777216 true "" ""
"Patch" 1.0 0 -5298144 true "" ""

SWITCH
74
322
242
355
random-diffusion?
random-diffusion?
0
1
-1000

MONITOR
964
389
1077
434
Mean Res. Time
mean effluent-rtd
1
1
11

MONITOR
875
389
961
434
Space Time
space-time
1
1
11

TEXTBOX
116
197
266
215
CONVECTION\n
11
0.0
1

TEXTBOX
120
264
270
282
DISPERSION\n
11
0.0
1

TEXTBOX
120
369
270
387
REACTION
11
0.0
1

PLOT
302
436
481
586
Production Rate
Time
Product/Tick
0.0
10.0
0.0
10.0
true
false
"" "ifelse ticks < (10 * sampling-window)\n  [set-plot-x-range 0 (ticks + 1)]\n  [set-plot-x-range (ticks - (10 * sampling-window)) (ticks + 1)]\n\nif effluent-product > 0 \n  [set-plot-y-range 0 (ceiling (effluent-product + 1))\n   plot effluent-product]"
PENS
"Production" 1.0 0 -16777216 true "" ""

TEXTBOX
484
592
713
662
Conversion has a time lag of one space-time so it often overshoots its steady state.
11
0.0
1

TEXTBOX
1136
290
1286
308
TRACER EXPERIMENTS
11
0.0
1

TEXTBOX
883
650
1181
732
MODEL LIMITS:\nConvection / Dispersion --> 0    :  \"Ideal CSTR\"\nConvection / Dispersion --> inf  :  \"Ideal PFR\"\nConvection / Reaction    --> 0    :  100% Conversion\nConvection / Reaction    --> inf  :  No Conversion
11
0.0
1

TEXTBOX
1169
649
1390
724
RES. TIME DIST.        CONC. PROFILE\n   Exponential                 Uniform    \n    Gaussian               Rxn Dependent\n        Any                         Uniform          \n        Any                         Uniform
11
0.0
1

TEXTBOX
882
635
1216
663
EXPECTATIONS FROM THE ANALYTICAL DISPERSION MODEL
11
0.0
1

SLIDER
65
427
237
460
collision-radius
collision-radius
0
10
1
0.1
1
NIL
HORIZONTAL

TEXTBOX
378
18
564
62
Reactants are blue\nProducts are red\nInerts are yellow\n
11
0.0
1

SWITCH
882
593
1070
626
non-dim-res-time?
non-dim-res-time?
0
1
-1000

MONITOR
672
389
766
434
Peclet
peclet
1
1
11

BUTTON
1000
135
1222
191
Export Parameters
export-results
NIL
1
T
OBSERVER
NIL
NIL
NIL
NIL
1

BUTTON
1000
78
1223
132
Import Patch Model Parameters
import-patch-model
NIL
1
T
OBSERVER
NIL
NIL
NIL
NIL
1

MONITOR
768
389
873
434
Damkohler
space-time * reaction-probability / 100
2
1
11

MONITOR
698
588
838
633
MSE Between Profiles
mean-sq-error reactant-profile reactant-profile-patches
2
1
11

TEXTBOX
325
75
391
109
Reactor Entrance
11
0.0
1

@#$#@#$#@
## WHAT IS IT?

This is an agent-based approach to modeling continuous-flow chemical reactors.  By using simple agent-level rules to recreate a number of known reaction engineering phenomena, the model is designed to serve as an educational tool for chemical engineering students.  With further validation and development, the model could in principle be used to design and simulate real chemical reactors, albeit at very high computational cost relative to equation-based methods.

The scope of the model is analogous to that of the convection-dispersion-reaction system taught in undergraduate chemical engineering courses.  Namely, fluid dynamics and bulk diffusion effects are rolled into a single diffusion mechanism named dispersion.

The model operates from the Lagrangian reference frame, as all notable actions are assigned to mobile turtles.  For the Eulerian reference frame, see the accompanying model in the RELATED MODELS section below.


## HOW IT WORKS

The reactor is oriented such that the feed stream enters at the left boundary and leaves at the right boundary. The model contains three types of molecular entities:  Reactants are blue turtles, Products are red turtles, and Inerts are yellow turtles.   

On setup, the far left column of patches sprout reactant at a user-specified concentration (number per patch).  If the FILL-REACTOR? option is switched on, all patches are filled with reactant in this manner.

A significant portion of the code was written for analysis purposes and is not relevant to the function of the reactor.  Consequently, all such code has been omitted:

At each tick: 
	
	

	1. Reactants react at a rate determined by the specified reaction mechanism.  Three reaction mechanisms are available:
		a. Concentration-Independent: Reactant turtles are randomly converted to products, with probability equal to the REACTION-PROBABILITY.
		b. Concentration-Dependent: Reactant turtles are randomly converted to products, with probability equal to the product of the REACTION-PROBABILITY and the number of other reactants within the reactant's COLLISION-RADIUS.
		c. Bimolecular: Pairs of reactant turtles are converted to products with probability equal to the REACTION-PROBABILITY, but only if two or more reactants are within the COLLISION-RADIUS.
	
		When a unimolecular reaction occurs, the reactant changes its breed to product.
		When a bimolecular reaction occurs, the primary reactant asks the other reactant within its COLLISION-RADIUS to die. It then changes its breed to product.	

	
	2. All molecules are affected by two modes of transport:
		a. Convection --> Molecules are uniformly transported downstream at the specified FEED-VELOCITY.  The molecules do not immediately move to their new position, but rather store their projected coordinates. 
		b. Diffusion --> Molecules undergo 1-D random walk diffusion by moving forward or backward.  If RANDOM-DIFFUSION? is on, the step size is a random distance less than the DIFFUSIVITY.  If RANDOM-DIFFUSION? is off, the step size is the DIFFUSIVITY. The molecules do not immediately move to their new position, but rather add their projected random walk to the coordinates projected during the convection step. 

	
	3. The effluent is analyzed:	
		a. All molecules with a projected position beyond the end of the reactor are added to the effluent stream agentset.
		b. The number and type of molecules entering and leading the reactor are stored such that they may be analyzed later.

	4. Molecular motion is executed
		a. All molecules in the effluent are asked to die.
		b. All molecules with projected position before the feed to the reactor are moved to the reactor inlet.
		c. All other molecules are moved to their projected position.
		d. All surviving molecules are asked to increment their residence time.

	5. The feed stream enters the reactor. The patches lining the reactor inlet are asked to sprout reactant at the user-specified concentration.






## HOW TO USE IT

The best way to the learn the model is to play around with parameters and view the results. The model is essentially 1-D, but has been constructed in 2-D so as to improve its interpretability.  To run the model:

	1. Select a desired FEED-CONCENTRATION. Note most results are normalized, so the main benefit of higher concentration is less noise in the resultant data.  The notable exception are the CONCENTRATION-DEPENDENT and BIMOLECULAR reaction mechanisms, which are density dependent.  Note that higher concentrations substantially increase computational expense.

	2. Select a FEED-VELOCITY to set the convection rate.

	3. Select a DIFFUSIVITY to set the dispersion rate.

	4. Select a REACTION-PROBABILITY, COLLISION-RADIUS, and REACTION-TYPE to set the reaction rate.  Note that the COLLISION-RADIUS is not relevant for the CONCENTRATION-INDEPENDENT mechanism.

	5. Click Go

	6. Vary the SAMPLING-WINDOW to change the time span over which effluent samples are averaged.  Larger windows are less sensitive to noise, but have longer dynamic responses.

	OPTIONAL: To run a tracer experiment, you can either click the TRACER-PULSE button or switch the TRACER-STEP? on.  

	OPTIONAL: To import parameters from the patch-based model, click the EXPORT PARAMETERS button in the patch-based model, then click the IMPORT PATCH MODEL PARAMETERS button on the turtle model's interface.  For the reverse, click EXPORT PARAMETERS on the turtle-model interface then click the IMPORT TURTLE MODEL PARAMETERS button on the patch model's interface.
	
The use of these optional features are described below under THINGS TO TRY.


## THINGS TO NOTICE

At very low diffusivities relative to feed velocity (convection >> dispersion), the residence time distribution (RTD) converges to a Gaussian.  In the limiting case, the RTD approaches the space-time.  This result is predicted by the analytical 1-D dispersion model, and is synonymous with the "ideal PFR" discussed in undergraduate chemical engineering courses.  In the opposite limit, as dispersion >> convection, the RTD converges to an exponential decay.  This result is also predicted by the 1-D dispersion model, and is synonymous to the "ideal CSTR."  Similarly results can be obtained by performing tracer response experiments.  The responses exhibited by the model to both pulse and step tracer experiments are identical to those predicted analytically.  Near the low dispersion limit, the variance of the Gaussian tracer pulse response is inversely proportional to the Peclet number.  Note that the dimensionless Peclet number expresses the ratio of convective transport to diffusive transport, and is quantitatively reported on the interface.

The Damkohler number expresses the ratio of reaction rate to convection rate, and is reported on the interface.  When this ratio is very high, reactants are rapidly consumed upon entering the reactor, resulting in a sharp drop in concentration near the inlet.  This scenario corresponds to an oversized reactor in which reactants remain in the reactor longer than necessary.  When the ratio is very low, convection dominates and conversion remains low leading to a uniform concentration profile throughout the reactor.  In this limit, reactants flow out of the reactor before they can react.

The model is able to capture dynamic behavior, including startup.  This is not typically coded into high level reactor models, and allows for some interesting analysis as well as potential for development of control strategies.

Several reaction mechanisms are provided, but these mechanisms are not specified in the deterministic rate expression form.  These stochastic mechanisms can be mapped to deterministic expressions using the export feature.



## THINGS TO TRY

A number of known limiting system states are listed in the upper right corner of the interface.  All of these phenomena can be reproduced in this model - try varying the parameters that determine the rates of convection, dispersion, and reaction to see if you can recreate these states.  Real reactors operate somewhere in the space between these limits, and can be reproduced by tuning the model parameters to match a real-world system.  In particular, FEED-VELOCITY, DIFFUSIVITY, and REACTION-PROBABILITY are directly proportional to the convection, dispersion, and reaction rates, respectively.

The SAMPLING-WINDOW is the time span over which effluent measurements are averaged.  What happens to the effluent measurement noise as the window increases?  What happens to the measurements' response times to perturbations in the system parameters? 

Inert tracers are used to characterize flow patterns in real reactors.  In a pulse experiment, an ephemeral pulse of inert tracer is added to the reactor feed stream, and the tracer concentration of the effluent is monitored.  In a step experiment, a step change is made to the steady state tracer concentration in the feed stream.  Try running a few of each experiment.  What does the tracer response tell you about the residence time distribution?  Could you characterize the relative rates of convection and dispersion using only this experimental tool?

A number of different reaction mechanisms are provided.  By using the EXPORT PARAMETERS feature and the accompanying patch-based model, can you map these stochastic reaction mechanisms to deterministic rate expressions?



## EXTENDING THE MODEL

Potential extensions could entail:

	1. Building a means to import a reaction network, complete with multiple products, reactants, and stoichiometries.
	2. Additional analytical tools, such as curve fitting.
	3. Residence time distributions or tracer pulse responses could be converted to probability density and cumulative distribution functions.



## RELATED MODELS

An accompanying patch-based model is available on the NetLogo Modeling Commons. This model handles everything from the perspective of stationary points in the reactor. :

http://modelingcommons.org/browse/one_model/4402#model_tabs_browse_info

  

## CREDITS AND REFERENCES

The model was created for EECS 472 by Sebastian Bernasek at Northwestern University. The latest version of this model is available on the NetLogo Modeling Commons:

http://modelingcommons.org/browse/one_model/4401#model_tabs_browse_info


@#$#@#$#@
default
true
0
Polygon -7500403 true true 150 5 40 250 150 205 260 250

airplane
true
0
Polygon -7500403 true true 150 0 135 15 120 60 120 105 15 165 15 195 120 180 135 240 105 270 120 285 150 270 180 285 210 270 165 240 180 180 285 195 285 165 180 105 180 60 165 15

arrow
true
0
Polygon -7500403 true true 150 0 0 150 105 150 105 293 195 293 195 150 300 150

box
false
0
Polygon -7500403 true true 150 285 285 225 285 75 150 135
Polygon -7500403 true true 150 135 15 75 150 15 285 75
Polygon -7500403 true true 15 75 15 225 150 285 150 135
Line -16777216 false 150 285 150 135
Line -16777216 false 150 135 15 75
Line -16777216 false 150 135 285 75

bug
true
0
Circle -7500403 true true 96 182 108
Circle -7500403 true true 110 127 80
Circle -7500403 true true 110 75 80
Line -7500403 true 150 100 80 30
Line -7500403 true 150 100 220 30

butterfly
true
0
Polygon -7500403 true true 150 165 209 199 225 225 225 255 195 270 165 255 150 240
Polygon -7500403 true true 150 165 89 198 75 225 75 255 105 270 135 255 150 240
Polygon -7500403 true true 139 148 100 105 55 90 25 90 10 105 10 135 25 180 40 195 85 194 139 163
Polygon -7500403 true true 162 150 200 105 245 90 275 90 290 105 290 135 275 180 260 195 215 195 162 165
Polygon -16777216 true false 150 255 135 225 120 150 135 120 150 105 165 120 180 150 165 225
Circle -16777216 true false 135 90 30
Line -16777216 false 150 105 195 60
Line -16777216 false 150 105 105 60

car
false
0
Polygon -7500403 true true 300 180 279 164 261 144 240 135 226 132 213 106 203 84 185 63 159 50 135 50 75 60 0 150 0 165 0 225 300 225 300 180
Circle -16777216 true false 180 180 90
Circle -16777216 true false 30 180 90
Polygon -16777216 true false 162 80 132 78 134 135 209 135 194 105 189 96 180 89
Circle -7500403 true true 47 195 58
Circle -7500403 true true 195 195 58

circle
false
0
Circle -7500403 true true 0 0 300

circle 2
false
0
Circle -7500403 true true 0 0 300
Circle -16777216 true false 30 30 240

cow
false
0
Polygon -7500403 true true 200 193 197 249 179 249 177 196 166 187 140 189 93 191 78 179 72 211 49 209 48 181 37 149 25 120 25 89 45 72 103 84 179 75 198 76 252 64 272 81 293 103 285 121 255 121 242 118 224 167
Polygon -7500403 true true 73 210 86 251 62 249 48 208
Polygon -7500403 true true 25 114 16 195 9 204 23 213 25 200 39 123

cylinder
false
0
Circle -7500403 true true 0 0 300

dot
false
0
Circle -7500403 true true 90 90 120

face happy
false
0
Circle -7500403 true true 8 8 285
Circle -16777216 true false 60 75 60
Circle -16777216 true false 180 75 60
Polygon -16777216 true false 150 255 90 239 62 213 47 191 67 179 90 203 109 218 150 225 192 218 210 203 227 181 251 194 236 217 212 240

face neutral
false
0
Circle -7500403 true true 8 7 285
Circle -16777216 true false 60 75 60
Circle -16777216 true false 180 75 60
Rectangle -16777216 true false 60 195 240 225

face sad
false
0
Circle -7500403 true true 8 8 285
Circle -16777216 true false 60 75 60
Circle -16777216 true false 180 75 60
Polygon -16777216 true false 150 168 90 184 62 210 47 232 67 244 90 220 109 205 150 198 192 205 210 220 227 242 251 229 236 206 212 183

fish
false
0
Polygon -1 true false 44 131 21 87 15 86 0 120 15 150 0 180 13 214 20 212 45 166
Polygon -1 true false 135 195 119 235 95 218 76 210 46 204 60 165
Polygon -1 true false 75 45 83 77 71 103 86 114 166 78 135 60
Polygon -7500403 true true 30 136 151 77 226 81 280 119 292 146 292 160 287 170 270 195 195 210 151 212 30 166
Circle -16777216 true false 215 106 30

flag
false
0
Rectangle -7500403 true true 60 15 75 300
Polygon -7500403 true true 90 150 270 90 90 30
Line -7500403 true 75 135 90 135
Line -7500403 true 75 45 90 45

flower
false
0
Polygon -10899396 true false 135 120 165 165 180 210 180 240 150 300 165 300 195 240 195 195 165 135
Circle -7500403 true true 85 132 38
Circle -7500403 true true 130 147 38
Circle -7500403 true true 192 85 38
Circle -7500403 true true 85 40 38
Circle -7500403 true true 177 40 38
Circle -7500403 true true 177 132 38
Circle -7500403 true true 70 85 38
Circle -7500403 true true 130 25 38
Circle -7500403 true true 96 51 108
Circle -16777216 true false 113 68 74
Polygon -10899396 true false 189 233 219 188 249 173 279 188 234 218
Polygon -10899396 true false 180 255 150 210 105 210 75 240 135 240

head-2
true
0
Polygon -16777216 true false 15 285 147 1 270 285
Polygon -1 true false 27 287 150 0 21 281 27 287
Polygon -1 true false 273 287 150 0 279 281 273 287

house
false
0
Rectangle -7500403 true true 45 120 255 285
Rectangle -16777216 true false 120 210 180 285
Polygon -7500403 true true 15 120 150 15 285 120
Line -16777216 false 30 120 270 120

leaf
false
0
Polygon -7500403 true true 150 210 135 195 120 210 60 210 30 195 60 180 60 165 15 135 30 120 15 105 40 104 45 90 60 90 90 105 105 120 120 120 105 60 120 60 135 30 150 15 165 30 180 60 195 60 180 120 195 120 210 105 240 90 255 90 263 104 285 105 270 120 285 135 240 165 240 180 270 195 240 210 180 210 165 195
Polygon -7500403 true true 135 195 135 240 120 255 105 255 105 285 135 285 165 240 165 195

line
true
0
Line -7500403 true 150 0 150 300

line half
true
0
Line -7500403 true 150 0 150 150

pentagon
false
0
Polygon -7500403 true true 150 15 15 120 60 285 240 285 285 120

person
false
0
Circle -7500403 true true 110 5 80
Polygon -7500403 true true 105 90 120 195 90 285 105 300 135 300 150 225 165 300 195 300 210 285 180 195 195 90
Rectangle -7500403 true true 127 79 172 94
Polygon -7500403 true true 195 90 240 150 225 180 165 105
Polygon -7500403 true true 105 90 60 150 75 180 135 105

plant
false
0
Rectangle -7500403 true true 135 90 165 300
Polygon -7500403 true true 135 255 90 210 45 195 75 255 135 285
Polygon -7500403 true true 165 255 210 210 255 195 225 255 165 285
Polygon -7500403 true true 135 180 90 135 45 120 75 180 135 210
Polygon -7500403 true true 165 180 165 210 225 180 255 120 210 135
Polygon -7500403 true true 135 105 90 60 45 45 75 105 135 135
Polygon -7500403 true true 165 105 165 135 225 105 255 45 210 60
Polygon -7500403 true true 135 90 120 45 150 15 180 45 165 90

sheep
false
15
Circle -1 true true 203 65 88
Circle -1 true true 70 65 162
Circle -1 true true 150 105 120
Polygon -7500403 true false 218 120 240 165 255 165 278 120
Circle -7500403 true false 214 72 67
Rectangle -1 true true 164 223 179 298
Polygon -1 true true 45 285 30 285 30 240 15 195 45 210
Circle -1 true true 3 83 150
Rectangle -1 true true 65 221 80 296
Polygon -1 true true 195 285 210 285 210 240 240 210 195 210
Polygon -7500403 true false 276 85 285 105 302 99 294 83
Polygon -7500403 true false 219 85 210 105 193 99 201 83

square
false
0
Rectangle -7500403 true true 30 30 270 270

square 2
false
0
Rectangle -7500403 true true 30 30 270 270
Rectangle -16777216 true false 60 60 240 240

star
false
0
Polygon -7500403 true true 151 1 185 108 298 108 207 175 242 282 151 216 59 282 94 175 3 108 116 108

target
false
0
Circle -7500403 true true 0 0 300
Circle -16777216 true false 30 30 240
Circle -7500403 true true 60 60 180
Circle -16777216 true false 90 90 120
Circle -7500403 true true 120 120 60

tree
false
0
Circle -7500403 true true 118 3 94
Rectangle -6459832 true false 120 195 180 300
Circle -7500403 true true 65 21 108
Circle -7500403 true true 116 41 127
Circle -7500403 true true 45 90 120
Circle -7500403 true true 104 74 152

triangle
false
0
Polygon -7500403 true true 150 30 15 255 285 255

triangle 2
false
0
Polygon -7500403 true true 150 30 15 255 285 255
Polygon -16777216 true false 151 99 225 223 75 224

truck
false
0
Rectangle -7500403 true true 4 45 195 187
Polygon -7500403 true true 296 193 296 150 259 134 244 104 208 104 207 194
Rectangle -1 true false 195 60 195 105
Polygon -16777216 true false 238 112 252 141 219 141 218 112
Circle -16777216 true false 234 174 42
Rectangle -7500403 true true 181 185 214 194
Circle -16777216 true false 144 174 42
Circle -16777216 true false 24 174 42
Circle -7500403 false true 24 174 42
Circle -7500403 false true 144 174 42
Circle -7500403 false true 234 174 42

turtle
true
0
Polygon -10899396 true false 215 204 240 233 246 254 228 266 215 252 193 210
Polygon -10899396 true false 195 90 225 75 245 75 260 89 269 108 261 124 240 105 225 105 210 105
Polygon -10899396 true false 105 90 75 75 55 75 40 89 31 108 39 124 60 105 75 105 90 105
Polygon -10899396 true false 132 85 134 64 107 51 108 17 150 2 192 18 192 52 169 65 172 87
Polygon -10899396 true false 85 204 60 233 54 254 72 266 85 252 107 210
Polygon -7500403 true true 119 75 179 75 209 101 224 135 220 225 175 261 128 261 81 224 74 135 88 99

wheel
false
0
Circle -7500403 true true 3 3 294
Circle -16777216 true false 30 30 240
Line -7500403 true 150 285 150 15
Line -7500403 true 15 150 285 150
Circle -7500403 true true 120 120 60
Line -7500403 true 216 40 79 269
Line -7500403 true 40 84 269 221
Line -7500403 true 40 216 269 79
Line -7500403 true 84 40 221 269

wolf
false
0
Polygon -16777216 true false 253 133 245 131 245 133
Polygon -7500403 true true 2 194 13 197 30 191 38 193 38 205 20 226 20 257 27 265 38 266 40 260 31 253 31 230 60 206 68 198 75 209 66 228 65 243 82 261 84 268 100 267 103 261 77 239 79 231 100 207 98 196 119 201 143 202 160 195 166 210 172 213 173 238 167 251 160 248 154 265 169 264 178 247 186 240 198 260 200 271 217 271 219 262 207 258 195 230 192 198 210 184 227 164 242 144 259 145 284 151 277 141 293 140 299 134 297 127 273 119 270 105
Polygon -7500403 true true -1 195 14 180 36 166 40 153 53 140 82 131 134 133 159 126 188 115 227 108 236 102 238 98 268 86 269 92 281 87 269 103 269 113

worm-head
true
0
Polygon -16777216 true false 30 150 150 0 255 150
Polygon -16777216 true false 30 150 150 300 255 150

worm-segment
true
0
Rectangle -16777216 true false 60 30 240 270

worm-segment-2
true
0
Circle -16777216 true false 15 15 270
Line -1 false 60 60 30 150
Polygon -1 true false 240 60 270 165 225 255 270 195 285 150 270 90
Polygon -1 true false 60 60 30 165 75 255 30 195 15 150 30 90

x
false
0
Polygon -7500403 true true 270 75 225 30 30 225 75 270
Polygon -7500403 true true 30 75 75 30 270 225 225 270

@#$#@#$#@
NetLogo 5.2.0-RC4
@#$#@#$#@
@#$#@#$#@
@#$#@#$#@
<experiments>
  <experiment name="NumberFans" repetitions="1" runMetricsEveryStep="true">
    <setup>setup</setup>
    <go>go</go>
    <timeLimit steps="100"/>
    <metric>red-contentness</metric>
    <metric>blue-contentness</metric>
    <enumeratedValueSet variable="minimum-proximity">
      <value value="0.5"/>
      <value value="0.75"/>
      <value value="1"/>
      <value value="1.25"/>
      <value value="1.5"/>
      <value value="1.75"/>
      <value value="2"/>
    </enumeratedValueSet>
    <enumeratedValueSet variable="number-blue-fans">
      <value value="500"/>
    </enumeratedValueSet>
    <enumeratedValueSet variable="number-red-fans">
      <value value="500"/>
    </enumeratedValueSet>
  </experiment>
</experiments>
@#$#@#$#@
@#$#@#$#@
default
0.0
-0.2 0 0.0 1.0
0.0 1 1.0 0.0
0.2 0 0.0 1.0
link direction
true
0
Line -7500403 true 150 150 90 180
Line -7500403 true 150 150 210 180

@#$#@#$#@
0
@#$#@#$#@
