[R-sig-dyn-mod] better implementation of variable species ode.2D model
Jeremy Chacon
chaco001 at umn.edu
Fri Feb 24 21:23:20 CET 2017
Thank you Thomas for your reply (not to mention your fantastic packages!).
I tried changing my function so that the data all get tranformed between a
1d vector and a (preallocated) 3d array (new code below), but when I
compared the two head-to-head, found no difference between my original
loop-list attempt and the newer loop-array attempt. So perhaps I am simply
hitting the point where, as you suggest, I move the function itself outside
of R. That said, I still get the sense I am doing something fundamentally
slow with my code, but can't quite figure out what that is.
Thank you again,
Jeremy
# example of variable-species ode.2D model, slow
rm(list = ls())
library(ReacTran)
# list implementation (old way)
variable_species_lv_2D = function(t, y, parms){
# y holds all the data. there are Nx * Ny boxes.
n_colonies = length(y) / (Nx*Ny) - 1
# the resource is always first in the data vector y
resource = matrix(nrow = Nx, ncol = Ny,
data = y[1:(Nx * Ny)])
# grab each consumer's data
consumers = list()
for (k in 1:n_colonies){
consumers[[k]] = matrix(nrow = Nx, ncol = Ny,
data = y[(1 + k * (Nx * Ny)): ((k + 1) * Nx * Ny)])
}
# get the diffusion for the resource
resource_diff = tran.2D(resource, grid = grid2D, D.grid =
D_resource_grid)$dC
# get the diffusion for each consumer
consumers_diff = list()
for (k in 1:n_colonies){
consumers_diff[[k]] = tran.2D(consumers[[k]], grid = grid2D, D.grid =
D_consumer_grid)$dC
}
# get the reaction for each consumer, and simultaneously
# subtract that from the resource
consumers_rxn = list()
resource_rxn = matrix(nrow = Nx, ncol = Ny,
data = 0)
for (k in 1:n_colonies){
consumers_rxn[[k]] = r * consumers[[k]] * resource
resource_rxn = resource_rxn - consumers_rxn[[k]]
}
# sum the effects of diffusion + reaction,
# and simultaneously concatenate all the data back
# into a single vector
dresource = resource_diff + resource_rxn
dAll = dresource
for (k in 1:n_colonies){
curr_dconsumer = consumers_diff[[k]] + consumers_rxn[[k]]
dAll = c(dAll, curr_dconsumer)
}
return(list(dAll))
}
# matrix implementation (new way)
diffusion_matrix = array(dim = c(Nx, Ny, n_consumers+1), data = 0)
reaction_matrix = array(dim = c(Nx, Ny, n_consumers+1), data = 0)
array_lv_2D = function(t, y, parms){
all_data_array = array(y, dim = c(Nx, Ny, n_consumers + 1))
# get the diffusion for the resource
diffusion_matrix[,,1] = tran.2D(all_data_array[,,1], grid = grid2D,
D.grid = D_resource_grid)$dC
# get the diffusion for each consumer
for (k in 2:(n_consumers+1)){
diffusion_matrix[,,k] = tran.2D(all_data_array[,,k], grid = grid2D,
D.grid = D_consumer_grid)$dC
}
# get the reaction for each consumer, and simultaneously
# subtract that from the resource
for (k in 2:(n_consumers+1)){
reaction_matrix[,,k] = r * all_data_array[,,k] * all_data_array[,,1]
reaction_matrix[,,1] = reaction_matrix[,,1] - reaction_matrix[,,k]
}
return(list(as.vector(diffusion_matrix + reaction_matrix)))
}
world_width = 100
Nx = Ny = 40
D_resource = 1
D_consumer = 1e-2
r = 0.01
Gridx = setup.grid.1D(L = world_width, N = Nx)
Gridy = setup.grid.1D(L = world_width, N = Ny)
grid2D = setup.grid.2D(Gridx, Gridy)
D_consumer_grid = setup.prop.2D(value = D_consumer, y.value = D_consumer,
grid = grid2D)
D_resource_grid = setup.prop.2D(value = D_resource, y.value = D_resource,
grid = grid2D)
n_consumers = 8
resource_start = matrix(nrow = Nx, ncol = Ny, data = 1000)
all_start = resource_start
for (k in 1:n_consumers){
curr_consumer_start = matrix(nrow = Nx, ncol = Ny, data = 0)
curr_consumer_start[sample(1:(Nx * Ny), 1)] = 1
all_start = c(all_start, curr_consumer_start)
}
times = seq(from = 0, to = 10, by = 1)
print(system.time(
out <- ode.2D(y = all_start,
times = times,
func = variable_species_lv_2D,
parms = NULL,
nspec = n_consumers + 1,
dimens = c(Nx, Ny),
lrw = 5e8,
hmax = 0.01)
))
print(system.time(
out <- ode.2D(y = all_start,
times = times,
func = array_lv_2D,
parms = NULL,
nspec = n_consumers + 1,
dimens = c(Nx, Ny),
lrw = 5e8,
hmax = 0.01)
))
# note both times are the same
On Fri, Feb 24, 2017 at 10:43 AM, Thomas Petzoldt <
thomas.petzoldt at tu-dresden.de> wrote:
> Am 24.02.2017 um 16:31 schrieb Jeremy Chacon:
>
>> Hello list,
>>
>> I am running ode.2D models (from deSolve, using ReacTran to manage
>> diffusion) in which I wish to have a variable number of "species"
>> (differential equations) which a single function can handle. I've managed
>> to come up with a method that uses lists and loops internally, see the
>> code
>> below for an example with a simpler reaction than I will be using in
>> practice.
>>
>> However, it's quite slow, especially when I scale up the number of species
>> (or the number of boxes, but that is expected).
>>
>> I would greatly appreciate any tips on how to better implement this idea.
>> I know if it was a "mean-field" model, I could simply use matrix algebra
>> inside the function to allow a variable number of species, but I'm not
>> sure
>> how that can be extended to a 2D situation.
>>
>
> 2D works nicely if you flatten your 2D matrix into a 1D vector (with
> "as.vector") and back to a matrix with "matrix" when needed. Reshaping
> matrices is very fast and my experience shows that also deSolve with
> multidimensional models can be fast, if vectorization is consequently used.
> If this is not possible, you may consider to use compiled code (C or
> Fortran).
>
> Regards, Thomas
>
> _______________________________________________
> R-sig-dynamic-models mailing list
> R-sig-dynamic-models at r-project.org
> https://stat.ethz.ch/mailman/listinfo/r-sig-dynamic-models
>
--
*___________________________________________________________________________Jeremy
M. Chacon, Ph.D.*
*Post-Doctoral Associate, Harcombe Lab*
*University of Minnesota*
*Ecology, Evolution and Behavior*
[[alternative HTML version deleted]]
More information about the R-sig-dynamic-models
mailing list