######################################################################################### ### R code for PCOMPBIOL-D-12-01525R1 / 10.1371/journal.pcbi.1002900: ################### ### C. Magnus. Virus Neutralisation: New Insights from Kinetic Neutralisation Curves. ### ######################################################################################### ##### 0. Definition of spike number distribution, eta ##### ETA_vars<-function(a,b,s.max){ # a 1st parameter of Beta-distribution # b 2nd parameter of Beta-distribution # s.max maximal number of spikes if(sum(c(a,b)<=0)!=0){ETA_vars<-rep(1/(s.max+1),s.max+1)} else{ if(sum(c(a,b)>=1)==2){ETA_vars<-dbeta(seq(from=0,to=1,by=1/s.max),a,b)/(sum(dbeta(seq(from=0,to=1,by=1/s.max),a,b)))} else{v<-dbeta(seq(from=0,to=1,by=1/s.max),a,b) if(v[1]==Inf){v[1]<-ceiling(v[2])} if(v[s.max+1]==Inf){v[s.max+1]<-ceiling(v[s.max])} ETA_vars<-v/(sum(v)) } } } meandos<-function(dos,s.max)sum(dos*0:s.max) vardos<-function(dos,s.max)sum(dos*(0:s.max-sum(dos*0:s.max))^2) rssab<-function(X,ew,v,s.max){ dos<-ETA_vars(X[1],X[2],s.max) (sum(dos*0:s.max)-ew)^2+(sum(dos*(0:s.max-sum(dos*0:s.max))^2)-v)^2 } fitab_m_var<-function(ew,v,s.max,start){ optim(start,rssab,ew=ew,v=v,s.max=s.max,) } dos_m_var<-function(ew,v,s.max,start){ bla<-fitab_m_var(ew,v,s.max,start) ETA_vars(bla$par[1],bla$par[2],s.max) } # HIV specific trimer number distribution, described in the article dos1449<-dos_m_var(14,49,100,c(5,2)) # equally distributed trimer numbers, used in "Other viral systems", especially for Figure 6 dos100<-c(rep(0,10),1) dos10218<-c(rep(0,2),rep(1/17,17)) dos360<-c(rep(0,36),1) dos36072<-rep(1/73,73) ######### 1. Models for three epitopes per trimer only ######### ##### elementary reaction model #### definition of the ODE - has to be uncommented when used # TRAB<-function(t,y,parms){ # with(as.list(c(parms,y)),{ # dTr <- k1off*TrAb-k1on*Tr*Ab # dAb <- k1off*TrAb + k2off*TrAb2 + k3off*TrAb3 - (k1on*Tr*Ab + k2on*TrAb*Ab + k3on*TrAb2*Ab) # dTrAb<- k1on*Tr*Ab + k2off*TrAb2 -(k1off*TrAb + k2on*TrAb*Ab) # dTrAb2<-k2on*TrAb*Ab + k3off*TrAb3 - (k2off*TrAb2 + k3on*TrAb2*Ab) # dTrAb3<-k3on*TrAb2*Ab - k3off*TrAb3 # der<-c(dTr,dAb,dTrAb,dTrAb2,dTrAb3) # list(der) # } # ) # } ##### stoichiometric reaction model ### definition of the ODE - has to be commented-out when the elementary reaction model is used TRAB<-function(t,y,parms){ with(as.list(c(parms,y)),{ dTr <- k1off*TrAb-k1on*Tr*Ab^3 dAb <- k1off*TrAb + k2off*TrAb2 + k3off*TrAb3 - (k1on*Tr*Ab^3 + k2on*TrAb*Ab^2 + k3on*TrAb2*Ab) dTrAb<- k1on*Tr*Ab^3 + k2off*TrAb2 -(k1off*TrAb + k2on*TrAb*Ab^2) dTrAb2<-k2on*TrAb*Ab^2 + k3off*TrAb3 - (k2off*TrAb2 + k3on*TrAb2*Ab) dTrAb3<-k3on*TrAb2*Ab - k3off*TrAb3 der<-c(dTr,dAb,dTrAb,dTrAb2,dTrAb3) list(der) } ) } abneutra_ODE<-function(dt,parms,ctr,cab,plot){ require(odesolve) # dt time steps, eg dt<-1:10^5 # parms kinetic parameters, eg parms<-c(1e+6,1e-5,5e+5,1e-5,1e+5,1e-5) # ctr trimer concentration # cab antibody concentration # plot TRUE or FALSE if a plot should be created or not names(parms)<-c("k1on","k1off","k2on","k2off","k3on","k3off") inits<-c(Tr=ctr,Ab=cab,TrAb=0,TrAb2=0,TrAb3=0) simulation<-lsoda(inits,dt,TRAB,parms=parms) if(plot){# plotting the results plot(dt,simulation[,"Tr"],type="l",col="blue",ylim=c(0,max(simulation[,c("Ab","Tr","TrAb","TrAb2","TrAb3")])),xlab="time",ylab="product concentrations") lines(dt,simulation[,"Ab"],type="l",col="black",lwd=2) lines(dt,simulation[,"TrAb"],type="l",col="red",lwd=2) lines(dt,simulation[,"TrAb2"],type="l",col="green",lwd=2) lines(dt,simulation[,"TrAb3"],type="l",col="magenta",lwd=2) legend(dt[10],max(simulation[,c("Ab","Tr","TrAb","TrAb2","TrAb3")]),legend=c("Tr","Ab","TrAb","TrAb2","TrAb3"),col=c("blue","black","red","green","magenta"),bty="n",lty=rep(1,5)) } return(simulation) } inf.virions.tit<-function(eta,TT,N,f0,f1,f2,f3,relativePI){ # eta: trimer number distribution, the last entry in this vector MUST be >0 # TT: stoichiometry of entry, must be >=1 # N: stoichiometry of neutralization # fi: fraction of trimers with i abs # relativePI: relative percent infectivity when TRUE. Not normalised with sum(eta[(TT:s.max)+1]) if FALSE. If the V/Vo is calculated by dividing with the total number of virions, relativeI=FALSE. If Vo is meant to be the number of infective virions (for example in infectivity experiments), this parameter is TRUE. s.max<-length(eta)-1 if(s.maxExample # plot(Example[,"time"],log(Example[,"perc.inf"],10),xlab="time",ylab="log(Vt/V0)",type="l") ###################### data fitting ###################### ### data extracted from McLain, L, Dimmock, N (1994). Single-hit and multi-hit kinetics of immunoblobullin-G neutralization of human immunodeficiency virus type-1 by monoclonal antibodies. J gen Virol 75: 1457-1460, Figure 2 # the percent infectivity is displayed as log(PI); PI=V_t/V_0 Tmcla<-data.frame(matrix(c(300, 600, 900, 1860, 2700, 3660, 5400, 7200, -0.109321, -0.195690, -0.350959, -0.460785, -0.582048, -0.680390, -0.825324, -1.010500), nrow=8, ncol=2, dimnames=list(1:8,c("Time","logPI")))) Tmclb<-data.frame(matrix(c(360, 660, 960, 1860, 2700, 3660, 5400, 7200, 0.00647773, -0.13034800, -0.30299300, -0.46268200, -0.61046300, -0.74030300, -1.08962000, -1.08661000), nrow=8, ncol=2, dimnames=list(1:8,c("Time","logPI")))) Tmclc<-data.frame(matrix(c(300, 660, 900, 1800, 2760, 3660, 5400, 7200, 0.00670391, 0.01340780, -0.00670391, -0.14078200, -0.34860300, -0.55642500, -0.87150800, -1.09944000), nrow=8, ncol=2, dimnames=list(1:8,c("Time","logPI")))) # definition of the residual sum of squares rss.knc<-function(parms,data,ctr,cab,N,TT,eta){ # parms: binding and dissociation parameters # data: matrix with two columns: Time, logPI, where Time is measured in SECONDS # ctr: concentration of trimers # cab: concentration of antibodies # N: stoichiometry of neutralisation # TT: stoichiometry of entry # eta: trimer number distribution trafo<-function(x){x} dt<-seq(0,max(data$Time),by=1) sum((trafo(10^(data$logPI))-trafo(kin.neut.curve.theo.b(dt,data$Time,parms,ctr,cab,N,TT,eta,TRUE)[,"perc.inf"]))^2) } # optimize residual sum of square knc.optim<-function(start,data,ctr,cab,N,TT,eta){ # start: starting value: log10 of the constants zw<-optim(start,function(parms) rss.knc(10^parms,data,ctr,cab,N,TT,eta),lower=c(0,-10,0,-10,0,-10),method="L-BFGS-B",control=list(factr=1e06)) matrix(c(zw$par,zw$value,zw$convergence),nrow=1,ncol=8,dimnames=list("",c("k1on","k1off","k2on","k2off","k3on","k3off","rss","conv"))) } #### example for calling this function # start<-c(8,-2,5,-3,0,-4) # data<-Tmcla # ctr<-4*10^(-5) # cab<-4.2*10^(-4) # N<-1 # TT<-2 # eta<-dos1449 # knc.optim(start,data,ctr,cab,N,TT,eta)