-
Notifications
You must be signed in to change notification settings - Fork 0
/
m_pmb_sigprior_v2.stan
252 lines (242 loc) · 9.32 KB
/
m_pmb_sigprior_v2.stan
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
functions {
real dirichletmultinomial_lpmf(int [] n, vector alpha) {
int N = sum(n);
real A = sum(alpha);
return lgamma(A) - lgamma(N + A) + sum(lgamma(to_vector(n) + alpha)) - sum(lgamma(alpha));
}
}
data {
// size variables
int<lower=0> m; // number of size classes
int<lower=0> nt; // number of timesteps
int<lower=0> nt_obs; // number of timesteps with observations
// model parameters and input
int<lower=0> dt; // delta t in minutes
real<lower=0> E[nt]; // vector of incident radiation values
real<lower=0> v_min; // size in smallest size class in um^-3
int<lower=0> delta_v_inv; // inverse of delta_v
// observations
real<lower=0> t_obs[nt_obs]; // the time of each observation
real<lower=0> obs[m,nt_obs]; // observations
int<lower=0> obs_count[m,nt_obs]; // count observations
// for cross-validation
int<lower=0, upper=1> i_test[nt_obs];
int prior_only;
//int<lower=0> start; // initial time in hours
}
transformed data {
int j;
real v_range;
real<lower=0> delta_v;
real<lower=0> dt_days; // dt in units of days
real<lower=0> dt_norm; // dt in units of days and doublings
real<lower=0> v[m+1]; // vector of (minimum) sizes for each size class
row_vector[m] v_mid; // vector of sizes for each size class
real<lower=0> v_diff[m-1];// vector of size-differences for first m-1 size classes
int<lower=0> t[nt]; // vector of times in minutes since start
int<lower=1, upper=nt> it_obs[nt_obs]; // the time index of each observation
int n_test = sum(i_test);
real exponent_max = 1.0;
j = 1 + delta_v_inv;
delta_v = 1.0/delta_v_inv;
dt_days = dt/1440.0;
dt_norm = dt/(1440.0 * (2^delta_v - 1.0));
for (i in 1:m+1){
v[i] = v_min*2^((i-1)*delta_v);
}
for (i in 1:m){
// using geometric mean
v_mid[i] = sqrt(v[i] * v[i+1]);
}
v_range = v_mid[m]-v_mid[1];
for (i in 1:m-1){
// difference between the centers for each class
v_diff[i] = v_mid[i+1] - v_mid[i];
}
// populate time vector
//t[1] = start*60;
t[1] = 0;
for (i in 2:nt){
t[i] = (t[i-1] + dt);
}
// populate index vector it_obs
for (k in 1:nt_obs){
for (i in 1:nt){
if (t_obs[k]>=t[i] && t_obs[k]<t[i]+dt){
it_obs[k] = i;
break;
}
}
}
}
parameters {
simplex[m-j+1] delta_incr;
real<lower=1.0, upper=1.0/dt_days> delta_max;
real<lower=0.0, upper=1.0/dt_norm> gamma_max;
real<lower=0.2, upper=1.0> rho_max;
real<lower=1, upper=1000> E_star;
real<lower=1e-10, upper=1.0> sigma;
real<lower=-exponent_max,upper=0> exponent_gamma;
// simplex[m] theta[nt_obs];
simplex[m] w_ini; // initial conditions
}
transformed parameters {
real divrate;
real<lower=0, upper=1.0/dt_days> delta[m-j+1];
matrix<lower=0>[m,nt_obs] mod_obspos;
real<lower=0> resp_size_loss[nt]; // record size loss due to respiration
real<lower=0> growth_size_gain[nt]; // record size gain due to cell growth
real<lower=0> max_size_gain[nt]; // record maximum possible size gain
real<lower=0> total_size[nt]; // record total size
real<lower=0> cell_count[nt]; // record relative cell count for each time step
{
// helper variables
vector[m] w_curr;
vector[m] w_next;
real delta_i = 0.0;
real gamma;
real gamma_sat;
real a;
real a_max;
real rho;
real sizelim_gamma[m];
real x;
int ito = 1;
// populate delta using delta_incr
delta[1] = delta_incr[1] * delta_max;
for (i in 1:m-j){
delta[i+1] = delta[i] + delta_incr[i+1] * delta_max;
}
// pre-compute size-limitations
if (exponent_gamma > 0){
for (i in 1:m){ // size-class loop
sizelim_gamma[i] = (v_mid[i]^exponent_gamma)/(v_mid[m]^exponent_gamma);
}
} else {
for (i in 1:m){ // size-class loop
sizelim_gamma[i] = (v_mid[i]^exponent_gamma)/(v_mid[1]^exponent_gamma);
}
}
w_curr = w_ini;
for (it in 1:nt){ // time-stepping loop
// record current solution
// here is another place where normalization could be performed
if (it == it_obs[ito]){
mod_obspos[,ito] = w_curr;
ito += 1;
if (ito > nt_obs){
// just needs to be a valid index
// cannot use break because resp_size_loss needs to be recorded
ito = 1;
}
}
w_next = rep_vector(0.0, m);
resp_size_loss[it] = 0.0;
growth_size_gain[it] = 0.0;
max_size_gain[it] = 0.0;
total_size[it] = v_mid * w_curr;
cell_count[it] = sum(w_curr);
for (i in 1:m){ // size-class loop
// compute delta_i
if (i >= j){
delta_i = delta[i-j+1] * dt_days;
}
// compute gamma_i
gamma = dt_norm * sizelim_gamma[i] * gamma_max * (1.0 - exp(-E[it]/E_star));
gamma_sat = dt_norm * sizelim_gamma[i] * gamma_max;
// compute rho_i
rho = dt_norm * gamma_max * rho_max;
// fill superdiagonal (respiration)
if (i >= j){
//A[i-1,i] = rho * (1.0-delta_i);
a = rho * (1.0-delta_i);
w_next[i-1] += a * w_curr[i];
resp_size_loss[it] += a * w_curr[i] * v_diff[i-1];
} else if (i > 1){
//A[i-1,i] = rho;
a = rho;
w_next[i-1] += a * w_curr[i];
resp_size_loss[it] += a * w_curr[i] * v_diff[i-1];
}
// fill subdiagonal (growth)
if (i == 1){
//A[i+1,i] = gamma;
a = gamma;
a_max = gamma_sat;
w_next[i+1] += a * w_curr[i];
growth_size_gain[it] += a * w_curr[i] * v_diff[i];
max_size_gain[it] += a_max * w_curr[i] * v_diff[i];
} else if (i < j){
//A[i+1,i] = gamma * (1.0-rho);
a = gamma * (1.0-rho);
a_max = gamma_sat * (1.0-rho);
w_next[i+1] += a * w_curr[i];
growth_size_gain[it] += a * w_curr[i] * v_diff[i];
max_size_gain[it] += a_max * w_curr[i] * v_diff[i];
} else if (i < m){
//A[i+1,i] = gamma * (1.0-delta_i) * (1.0-rho);
a = gamma * (1.0-delta_i) * (1.0-rho);
a_max = gamma_sat * (1.0-delta_i) * (1.0-rho);
w_next[i+1] += a * w_curr[i];
growth_size_gain[it] += a * w_curr[i] * v_diff[i];
max_size_gain[it] += a_max * w_curr[i] * v_diff[i];
}
// fill (j-1)th superdiagonal (division)
if (i >= j){
//A[i+1-j,i] = 2.0*delta_i;
a = 2.0*delta_i;
w_next[i+1-j] += a * w_curr[i];
}
// fill diagonal (stasis)
if (i == 1){
//A[i,i] = (1.0-gamma);
a = (1.0-gamma);
w_next[i] += a * w_curr[i];
} else if (i < j){
//A[i,i] = (1.0-gamma) * (1.0-rho);
a = (1.0-gamma) * (1.0-rho);
w_next[i] += a * w_curr[i];
} else if (i == m){
//A[i,i] = (1.0-delta_i) * (1.0-rho);
a = (1.0-delta_i) * (1.0-rho);
w_next[i] += a * w_curr[i];
} else {
//A[i,i] = (1.0-gamma) * (1.0-delta_i) * (1.0-rho);
a = (1.0-gamma) * (1.0-delta_i) * (1.0-rho);
w_next[i] += a * w_curr[i];
}
}
/*
// use this check to test model consistency when division is set to "a = delta_i;" (no doubling)
if (fabs(sum(w_next)-1.0)>1e-12){
reject("it = ",it,", sum(w_next) = ", sum(w_next), ", rho =", rho)
}
*/
// do not normalize population here
w_curr = w_next;
}
divrate = log(sum(w_curr))*60*24/(nt*dt); // daily division rate
}
}
model {
vector[m] alpha;
// priors
gamma_max ~ normal(5.0, 2.0) T[0, 1.0/dt_norm];
rho_max ~ normal(0.5, 0.1) T[0, 1.0];
delta_max ~ normal(40.0, 10.0) T[0, 1.0/dt_days];
E_star ~ normal(150.0, 100.0) T[0,];
exponent_gamma ~ normal(-0.6, 0.1);
sigma ~ exponential(1.0);
delta_incr ~ dirichlet(rep_vector(1.0, m-j+1));
w_ini ~ dirichlet(rep_vector(1.0, m));
// fitting observations
if (prior_only == 0){
for (it in 1:nt_obs){
if(i_test[it] == 0){
alpha = mod_obspos[:,it]/sum(mod_obspos[:,it]) / sigma + 1;
// theta[it] ~ dirichlet(alpha);
target += dirichletmultinomial_lpmf(obs_count[:, it] | alpha);
}
}
}
}