# Analytical solution for diffusion of CO2 into the intercellular air space mytime = seq(0, 10, 0.125) # this gives our time series with start, end and interval CO2i_initial = 300 # set the initial intracellular CO2 to 300 (the units are parts per million, ppm) CO2outside = 360 # set the constant atmoshperic CO2 to 360 ppm conductance = 1 # set the conductance, determined by size, number and opening/closing of stomata # integrated rate equation to evaluate intracellular CO2 CO2inside = CO2outside+(CO2i_initial-CO2outside)*exp(-1.56*conductance*mytime) # Plot CO2i vs time as a line plot, and give the plot a title plot(mytime, CO2inside, type="l", main="Diffusion into cell") --------------------------------------------- # Euler method for numerical integration using an example of a simple exponential equation # We need to do the following things: # 1) Create the function that calculates the value of our dependent variable, Y, at each time point, # based on the previous value and a formula for how the value is changing (slope or first # differential). # 2) Assign values for independent variables and initial values # 3) Call our function (with parameters), and assign the result to a variable # 4) Plot the result, and compare the numerical result with the analytical solution # Y is set as a function that requires two parameters: # "yinit" is the initial value of Y at time 0. Since our formula will only calculate a new value # of Y based on the previous value, we need to provide an initial value # "x" is our independent variable, time, which is a vector of values containing each of the time # points at which we will calculate our dependent variable # The function "y" does four things: # 1) assigns the initial value to (the result) "y" # 2) calculates how many times it needs to calculate a value for "y", and how big the time step is # 3) loops through the calculation of "y" at each time point by first calculating the change in y, # as "dy", and then adding the change to the previous value of y, "y[i-1]". # 4) returns the resulting vector of y values y = function(yinit,x){ y = c(yinit) # Initial value passed to function # Divide x interval into n steps of length dx # Doing this here, inside the function, will allow us to evaluate y easily over different # intervals by passing different vectors of time points to the function n = length(x) # calculate how many times "y" will need to be evaluated dx=(x[n]-x[1])/(n-1) # calculate the size of the time step, this assumes that all time-steps # are the same size # Set-up a loop to calculate "y" at each time point. We don't need to calculate it at the first # time point, t=0, because we provided an initial value, so we start at the second time point for (i in 2:n) { dy = f(x[i-1])*dx # the function "f" is defined below for the differential rate equation y[i] = y[i-1]+dy # calculate "y" as HAVE = HAD + CHANGE } return(y) } f = function(x) {x^2} # This is the differntial rate equation (the slope, or first derivative, # at time xi), that describes the rate of change in y given a value of x # Create a vector of time points xinit = 0 # start time xmax = 2 # end time dx = .01 # time-step x = seq(xinit,xmax,dx) # vector of time points yinit = 0 # initial value of the dependent variable x1 = x # I don't know why this assignment was necessary y1 = y(yinit,x) # call our function "y" with it's needed parameters, and assign the result to "y1" plot(x1,y1, type="l") # Plot the result as a line plot, (again, I don't know why "x1" is necessary) lines(spline(x, x^3/3, n=201), col="red") # Add a plot of (x cubed over 3) to the plot, which is the # integrated version of this differential. Since our # differential equation (x squared) didn't include any # variable quantities, we can integrate it analytically # and compare it to our numerically integrated result. # Now try changing the time-step, "dx", and see how well # the numerical and analytical solutions compare. --------------------------------------------- # Euler method version of photosynthesis model - uses finite difference equations and quantity # values at one time-point to estimate the change in each quantity that will have occured by the # next time-point # Constants CO2out = 360; g = 1; kdiff = 1.56*g; PAR = 300 dt = .005; tmax = 10 # time-step interval, and ending time t = seq(0,tmax,dt); n = tmax/dt+1 # create a vector of time points, and calculate the number of # time points at which our quantities will be estimated # Create a matrix which will contain all of our dependant variables at each time point # row 1 = x[1,] =CO2i # row 2 = x[2,] = PGA # row 3 = x[3,] = G3P # row 4 = x[4,] = sucrose x = matrix(rep(0,4*n),nrow=4,ncol=n) x[1,1] = 300; x[2,1] = 0; x[3,1] = 0 ;x[4,1] = 0 # Initial values for(i in 2:n){ # don't need to calculate our intital values we just assigned so we loop up from 2 # evaluate each of the finite difference equations representing the change in our four # dependant variables dx1 = 1.56*g*(CO2out-x[1,i-1])*dt - ((100*(x[1,i-1]-40.4))/(x[1,i-1]+300*(1.7)))*dt dx2 = ((100*(x[1,i-1]-40.4))/(x[1,i-1]+300*(1.7)))*dt - ((0.064*PAR*(x[1,i-1]-40.4))/(x[1,i-1]+80.8))*dt dx3 = ((0.064*PAR*(x[1,i-1]-40.4))/(x[1,i-1]+80.8))*dt - min(50, x[3,i-1])*dt dx4 = min(50, x[3,i-1])*dt # take the smaller value of 50 or the amount of G3P on the last step # now determine the new value of each quantity as HAVE = HAD + CHANGE x[1,i] = x[1,i-1]+dx1 # CO2in x[2,i] = x[2,i-1]+dx2 # PGA x[3,i] = x[3,i-1]+dx3 # G3P x[4,i] = x[4,i-1]+dx4 # sucrose } plot(t, x[1,], type="l", lty=1, main="Photosynthesis Time Course", xlab="t/s", ylab="Conc/M", ylim=c(0,375), bty="l") lines(t, x[2,], lty=2) lines(t, x[3,], lty=3) lines(t, x[4,], lty=4) text(8, 325, "CO2in") text(8, 185, "PGA") text(8, 25, "G3P") text(8, 115, "Sucrose") # flux equations from the Stella model # diffusion - 1.56*g*(Ca-Ci) # fixation - (100*(Ci-40.4))/(Ci+300*(1.7)) # reduction - (0.064*PAR*(Ci-40.4))/(Ci+80.8) # sucrose formation - MIN(50,G3P) --------------------------------------------- # Improved Euler Method, or Runge-Kutta 2 - # Use the rate of change and quantity value at prior time-point to estimate the change in # quantity, as in the Euler method above # then calculate the change based on the estimated quantity at the end of the time-step # use the average of the two estimated changes, at the start and end of the interval, as the # change over the interval # Constants CO2out = 360; g = 1; kdiff = 1.56*g; PAR = 300 dt = .05; tmax = 10 # time-step interval, and ending time (larger steps) t = seq(0,tmax,dt); n = tmax/dt+1 # create a vector of time points, and calculate the number of # time points at which our quantities will be estimated # Create a matrix which will contain all of our dependant variables at each time point x = matrix(rep(0,4*n),nrow=4,ncol=n) x[1,1] = 300; x[2,1] = 0; x[3,1] = 0 ;x[4,1] = 0 # Initial values for(i in 2:n){ # Calculate the estimated change in each quantity using the finite difference equations with # the values of the quantities at the prior time-point dx1a = 1.56*g*(CO2out-x[1,i-1])*dt - ((100*(x[1,i-1]-40.4))/(x[1,i-1]+300*(1.7)))*dt dx2a = ((100*(x[1,i-1]-40.4))/(x[1,i-1]+300*(1.7)))*dt - ((0.064*PAR*(x[1,i-1]-40.4))/(x[1,i-1]+80.8))*dt dx3a = ((0.064*PAR*(x[1,i-1]-40.4))/(x[1,i-1]+80.8))*dt - min(50, x[3,i-1])*dt dx4a = min(50, x[3,i-1])*dt # take the smaller value of 50 or the amount of G3P on the last step # Calculate the Euler estimated quantities at the current time-point x1b = x[1,i-1]+dx1a # CO2in x2b = x[2,i-1]+dx2a # PGA x3b = x[3,i-1]+dx3a # G3P x4b = x[4,i-1]+dx4a # sucrose # Calculate the change in quantities based on the Euler estimated quantities dx1b = 1.56*g*(CO2out-x1b)*dt - ((100*(x1b-40.4))/(x1b+300*(1.7)))*dt dx2b = ((100*(x1b-40.4))/(x1b+300*(1.7)))*dt - ((0.064*PAR*(x1b-40.4))/(x1b+80.8))*dt dx3b = ((0.064*PAR*(x1b-40.4))/(x1b+80.8))*dt - min(50, x3b)*dt dx4b = min(50, x3b)*dt # take the smaller value of 50 or the amount of G3P on the last step # average the two rates of change to determine the improved Euler (RK2) estimate of the change dx1 = (dx1a+dx1b)/2 dx2 = (dx2a+dx2b)/2 dx3 = (dx3a+dx3b)/2 dx4 = (dx4a+dx4b)/2 # calculate the improved Euler (RK2) estimate of each quantity at the new time-point x[1,i] = x[1,i-1]+dx1 # CO2in x[2,i] = x[2,i-1]+dx2 # PGA x[3,i] = x[3,i-1]+dx3 # G3P x[4,i] = x[4,i-1]+dx4 # sucrose } plot(t, x[1,], type="l", lty=1, main="Photosynthesis Time Course", xlab="t/s", ylab="Conc/M", ylim=c(0,375), bty="l") lines(t, x[2,], lty=2) lines(t, x[3,], lty=3) lines(t, x[4,], lty=4) text(8, 325, "CO2in") text(8, 185, "PGA") text(8, 25, "G3P") text(8, 115, "Sucrose") --------------------------------------------- # Using the "rk4" Runge-Kutta 4 solver function in the "odesolve" package psmodel <- function(t, x, parms) { ci <- x[1] # CO2 internal pga <- x[2] # g3p <- x[3] # glucose 3 phoshpate suc <- x[4] # sucrose with(as.list(parms),{ import <- approx(signal$times, signal$import, t)$y dci <- 1.56*g*(CO2out-x1b)*dt - ((100*(x1b-40.4))/(x1b+300*(1.7)))*dt dpga <- c*s*p - d*k*p dg3p <- e*p*k - f*k dsuc <- e*p*k - f*k res<-c(ds, dp, dk) list(res) }) } parms = c(CO2out = 360; g = 1; kdiff = 1.56*g; PAR = 300)