-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProblemGenerator.jl
More file actions
290 lines (247 loc) · 11.7 KB
/
ProblemGenerator.jl
File metadata and controls
290 lines (247 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#Import Packages needed for simulation
using DifferentialEquations, Sundials #For implementing Differential equations
using LinearAlgebra, SparseArrays, Distributions, Statistics, Random #Linear Algebra and Statistics
using CSV, DataFrames #Data handling
using StatsPlots #For graphing
using JLD2, FileIO #Saving simulations
###############################################################
# 1. Define all the constants that will not change
###############################################################
#Constants for all cells
const N=200 #number of grid points along one dimensions
const nCells = N^2 #number of cells in the simulation
const cellVol = 3e-12 #Cell Volume (liters)
const Na = 6.02e23 #Avagadro's number
const species = 14 #Number of states within each cell (including virus)
const moi = 1.0e-2 #Multicity of infection
const cellIndicies = CartesianIndices(zeros(N,N)) #set of indices for looping through every cell
#Function that converts molecules to nM
m2c(molecule) = @. 1e9*molecule/(cellVol*Na)
#Constants for all simulations
const tspan = (0.0,48.0) #Time span for simulation
const ΔIFNβ = zeros(N,N) #Define memory space to hold the Laplacian
const statesNames = ["cGAS","DNA","Sting","cGAMP","IRF3","IFNbm","IFNb","STAT",
"SOCSm","IRF7m","TREX1m","IRF7","TREX1","Virus"] #for plotting
const θNames = [:k1f, :k1r, :k3f, :k3r, :k4f, :kcat5, :Km5, :k5r, :kcat6, :Km6, :kcat7,
:Km7, :kcat8, :Km8, :k8f, :k9f, :k10f1, :k10f2, :k11f, :k12f, :k13f, :k6f, :kcat2,
:Km2, :τ4, :τ6, :τ7, :τ8, :τ9, :τ10, :τ11, :τ12, :τ13, :k14f,:τ14] #Parameter names
#These species are not starting at zero
const nonZeroSpeciesIdx = [1,3,5] #cGAS, Sting, IRF3
const nonZeroSpeciesValues = m2c([1e3, 1e3, 1e4]) #convert to concentration
#This function modifies initial values by adding guassian noise
#Standard deviation is same magnitude as mean
# If p=0.5 then: (u₁=0.1 μ₂=100) → (σ₁=0.05 σ₂=50) instead of (σ₁=0.5 σ₂=0.5)
AddNoise2States(p) = [truncated(Normal(μ,p*μ),0,Inf) for μ in nonZeroSpeciesValues]
#Often it is useful to pass parameters between functions during the ODE solve,
#This struct hold all the parameters that we need to keep track of
mutable struct ParContainer{T}
par::T #Rate Constants
mass::Vector{Array{Float64,2}} #Mass balances
deathParameter::Array{Float64,2} # 0 or 1 indicating cell is dead
DNAReplicate::Int64 #0 if ISD, 1 if Virus
cellsInfected::Array{Float64,2} #Time cell was infected
cellsDead::Array{Float64,2} #Time cell was killed
infectFirstAttempt::BitArray{2} #Has cell tried to infect neighbors?
end
###############################################################
# 2. Helper function that discretizes the Laplacian
###############################################################
#∇u is updated with Laplacian estimate from u
function ∇²(Δu,u)
#Get dimensions of the input and define some constants
n1, n2 = size(u)
Δx = 32.0 #Grid spacing (diameter of cell in μm)
D=97.5*3600.0 #Diffusion coefficient (μm^2/hr)
h = D/Δx^2
# internal nodes
for j = 2:n2-1
for i = 2:n1-1
@inbounds Δu[i,j] = h*(u[i+1,j] + u[i-1,j] + u[i,j+1] + u[i,j-1] - 4*u[i,j])
end
end
# left/right edges
for i = 2:n1-1
@inbounds Δu[i,1] = h*(u[i+1,1] + u[i-1,1] + 2*u[i,2] - 4*u[i,1])
@inbounds Δu[i,n2] = h*(u[i+1,n2] + u[i-1,n2] + 2*u[i,n2-1] - 4*u[i,n2])
end
# top/bottom edges
for j = 2:n2-1
@inbounds Δu[1,j] = h*(u[1,j+1] + u[1,j-1] + 2*u[2,j] - 4*u[1,j])
@inbounds Δu[n1,j] = h*(u[n1,j+1] + u[n1,j-1] + 2*u[n1-1,j] - 4*u[n1,j])
end
# corners
@inbounds Δu[1,1] = h*(2*(u[2,1] + u[1,2]) - 4*u[1,1])
@inbounds Δu[n1,1] = h*(2*(u[n1-1,1] + u[n1,2]) - 4*u[n1,1])
@inbounds Δu[1,n2] = h*(2*(u[2,n2] + u[1,n2-1]) - 4*u[1,n2])
@inbounds Δu[n1,n2] = h*(2*(u[n1-1,n2] + u[n1,n2-1]) - 4*u[n1,n2])
end
###############################################################
# 3. This is the ODE model for the cGAS pathway
###############################################################
# Define the discretized PDE as an ODE function
function Model!(du,u,p,t)
#Species
cGAS = @view u[:,:,1]
DNA = @view u[:,:,2]
Sting = @view u[:,:,3]
cGAMP = @view u[:,:,4]
IRF3 = @view u[:,:,5]
IFNβm = @view u[:,:,6]
IFNβ = @view u[:,:,7]
STAT = @view u[:,:,8]
SOCSm = @view u[:,:,9]
IRF7m = @view u[:,:,10]
TREX1m = @view u[:,:,11]
IRF7 = @view u[:,:,12]
TREX1 = @view u[:,:,13]
Virus = @view u[:,:,14]
#Derivatives
d_cGAS = @view du[:,:,1]
d_DNA = @view du[:,:,2]
d_Sting = @view du[:,:,3]
d_cGAMP = @view du[:,:,4]
d_IRF3 = @view du[:,:,5]
d_IFNβm = @view du[:,:,6]
d_IFNβ = @view du[:,:,7]
d_STAT = @view du[:,:,8]
d_SOCSm = @view du[:,:,9]
d_IRF7m = @view du[:,:,10]
d_TREX1m = @view du[:,:,11]
d_IRF7 = @view du[:,:,12]
d_TREX1 = @view du[:,:,13]
d_Virus = @view du[:,:,14]
#Parameters
k1f, k1r, k3f, k3r, k4f, kcat5, Km5, k5r, kcat6, Km6, kcat7, Km7, kcat8, Km8, k8f, k9f, k10f1, k10f2, k11f, k12f, k13f, k6f, kcat2, Km2, τ4, τ6, τ7, τ8, τ9, τ10, τ11, τ12, τ13, k14f, τ14 = p.par
#Constants from the mass balances
cGAStot, Stingtot, IRF3tot = p.mass
#Which cells are dead?
💀 = p.deathParameter
#Should DNA be allowed to replicate (only with virus, not with ISD)
🔁 = p.DNAReplicate
#Calculate the diffusion of IFNβ
∇²(ΔIFNβ,IFNβ)
#Update derivatives for each species according to model
@. d_cGAS = -k1f*cGAS*DNA + k1r*(cGAStot - cGAS)
@. d_DNA = -k1f*cGAS*DNA + k1r*(cGAStot - cGAS) - kcat2*TREX1*DNA / (Km2 + DNA) + 🔁*💀*DNA*(0.55-DNA)/0.55
@. d_Sting = -k3f*cGAMP*Sting + k3r*(Stingtot - Sting)
@. d_cGAMP = k4f*(cGAStot - cGAS) - k3f*cGAMP*Sting + k3f*(Stingtot - Sting) - τ4*cGAMP
@. d_IRF3 = -kcat5*IRF3*(Stingtot - Sting) / (Km5 +IRF3) + k5r*(IRF3tot - IRF3)
@. d_IFNβm = 💀*kcat6*(IRF3tot - IRF3) / (Km6 + (IRF3tot - IRF3)) + 💀*k6f*IRF7 - τ6*IFNβm
@. d_IFNβ = 💀*kcat7*IFNβm / (Km7 + IFNβm) - τ7*IFNβ + ΔIFNβ #Add the diffusion in here
@. d_STAT = 💀*kcat8*IFNβ / (Km8 + IFNβ) * 1.0/(1.0+k8f*SOCSm) - τ8*STAT
@. d_SOCSm = 💀*k9f*STAT - τ9*SOCSm
@. d_IRF7m = 💀*k10f1*STAT + 💀*k10f2*IRF7 - τ10*IRF7m
@. d_TREX1m = 💀*k11f*STAT - τ11*TREX1m
@. d_IRF7 = 💀*k12f*IRF7m - τ12*IRF7
@. d_TREX1 = 💀*k13f*TREX1m - τ13*TREX1
@. d_Virus = 💀*k14f*DNA - τ14*Virus
end
###############################################################
# 4. Set up function that will return an ODE problem to solve
###############################################################
function ModelSetup(infectionMethod,IFNStoch,Hetero)
#Parameter values for the ODEs
θVals = [2.6899, 4.8505, 0.0356, 7.487, 517.4056, 22328.3852, 11226.3682,0.9341,
206.9446, 10305.461, 47639.70295,3.8474, 13.006, 78.2048, 0.0209,
0.0059, 0.001, 0.0112, 0.001, 99.9466, 15.1436,0.0276, 237539.3249,
61688.259, 0.96, 0.347, 12.2428736,1.2399, 1.5101, 0.347, 0.165, 6.9295,
0.0178]
θVirus = [1.0, 1.0] # k14f τ14 (Virus Parameters)
append!(θVals,θVirus) #Append the virus parameters to the orginal parameters
#Should IFN signaling be stochastic?
if IFNStoch == :Stochastic
#Keep most parameters the same
θ = fill.(θVals,N,N)
#kcat8 produces IFN, make it nonzero ~20% of the time (can be changed later)
θ[11] .= θVals[11] .* rand(Bernoulli(0.2),N,N)
else
θ = θVals #Just keep the parameters as is (same for each cell)
end
#Now we need to deal with the initial condition
#Define the initial conditions
u0 = zeros(N,N,species)
if Hetero == :Hetero
noiseDistributions = AddNoise2States(0.5)
for (i,index) in enumerate(nonZeroSpeciesIdx)
u0[:,:,index] = rand(noiseDistributions[i],N,N)
end
else
#Loop through non-zero species and update their concentrations
for (idx,val) in zip(nonZeroSpeciesIdx,nonZeroSpeciesValues)
u0[:,:,idx] .= val
end
end
#Finally need to set the DNA initial condition
if infectionMethod == :ISD
#Define a region on the domain where cells will be infected
circleOrigin = [0,0] #Where is the center of the drop?
circleRadiusSquared = N^2 #How big is the drop?
#Calculate squared distances
sqDist(x,c) = reduce(+, @. (x-c)^2)
#Loop though cells and check if they are infected
for currentCell in cellIndicies
#Are the cells inside the infected region?
if sqDist([currentCell[1],currentCell[2]],circleOrigin) ≤ circleRadiusSquared
u0[currentCell,2] = m2c(1e3)
end
end
elseif infectionMethod == :Virus
#Assume a poisson ditribution to randomly choose each cell's level of infection
probDistInfected = Poisson(moi)
u0[:,:,2] = @. m2c(1e3*rand(probDistInfected,N,N))
end
#Need to wrap everything up into the ParContainer struct
mass = [u0[:,:,i] for i in nonZeroSpeciesIdx]
deathParameter = ones(N,N)
DNAReplicate = infectionMethod==:ISD ? 0 : 1
#Keep track of infected cells (save time when infected, Inf means not infected)
cellsInfected = fill(Inf,N,N) #Make constant when not testing
cellsInfected[findall(u0[:,:,2] .> 0.0), 1] .= 0.0
#Keep track of time of death (TOD)
cellsDead = fill(Inf,N,N) #Inf implies alive
#Create an array that keeps track of whether or not a cell has tried to infect neighbors
infectFirstAttempt = trues(N,N)
#Create an instance of the structures
θ = ParContainer(
θ, #Rate constants
mass, #Mass balances
deathParameter, # cell is dead? (1==alive, 0==dead)
DNAReplicate, #can DNA replicate? (1 if virus)
cellsInfected, #Time cell was infected
cellsDead, #Time cell was killed
infectFirstAttempt) #Has cell tried to infect neighbors?)
return ODEProblem(Model!,u0,tspan,θ)
end
###############################################################
# 5. Function to count the cell states (healthy,infected,dead)
###############################################################
function cellStates(t,θ)
#Number of healthy cells at time t
totaHealthy = sum(θ.cellsInfected .> t)
#Number of dead cells at time t
totalDead = sum(θ.cellsDead .< t)
#Number of infected cells at time t
totalInfected = nCells - totaHealthy - totalDead
return [totaHealthy,totalInfected,totalDead]
end
###############################################################
# 6. Display Information about the Parameter Container
###############################################################
function Base.show(io::IO,p::ParContainer)
#Is simulation det/stoch and homo/hetero
attribute1 = isa(p.par[1],Array) ? "Stochastic" : "Deterministic" #True if stochastic
attribute2 = all(p.mass[1][1].==p.mass[1]) ? "Homogeneous" : "Heterogeneous" #True is homo
println("Cell Population: ",attribute1," and ",attribute2)
#What is the Input for DNA?
modelInput = p.DNAReplicate==1 ? "Virus" : "ISD"
println("Model Input: ",modelInput)
#How many cells initially infected?
println("Initially Infected: ",100.0*sum(p.cellsInfected.==0.0)/nCells, "%")
println("Currently Infected: ",100.0*sum(p.cellsInfected.<Inf)/nCells, "%")
#How many cells are initially dead?
println("Dead: ",100.0*((nCells - sum(p.cellsDead.==Inf))/nCells), "%")
#How many cells are not responsive to infection?
if attribute1 == "Stochastic"
println("Cells with kcat7=0: ",100.0*sum(p.par[11].==0.0)/nCells, "%")
end
end