summaryrefslogtreecommitdiff
path: root/CakeHelpers.R
blob: cc69509302530deb06669726f00bd27baed8b8ce (plain) (blame)
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
# $Id$
# Some of the CAKE R modules are based on mkin, 
# Developed by Tessella Ltd for Syngenta: Copyright (C) 2011-2020 Syngenta
# Tessella Project Reference: 6245, 7247, 8361, 7414, 10091

#    The CAKE R modules are free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
# 
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
# 
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Shifts parameters slightly away from boundaries specified in "lower" and 
# "upper" (to avoid computational issues after parameter transforms in modFit).
ShiftAwayFromBoundaries <- function(parameters, lower, upper) {
  parametersOnLowerBound = which(parameters == lower)
  parameters[parametersOnLowerBound] <- parameters[parametersOnLowerBound] * (1 + .Machine$double.eps) + .Machine$double.xmin

  parametersOnUpperBound = which(parameters == upper)
  parameters[parametersOnUpperBound] <- parameters[parametersOnUpperBound] * (1 - .Machine$double.neg.eps) - .Machine$double.xmin

  return(parameters)
}

# Adjusts stated initial values to put into the ODE solver.
#
# odeini: The initial values to adjust (in the form that would be fed into the ode function).
# cake.model: The expression of the model that we are solving.
# odeparms: The parameters for the ODE (in the form that would be fed into the ode function).
#
# Returns: Adjusted initial values.
AdjustOdeInitialValues <- function(odeini, cake.model, odeparms) {
  odeini.names <- names(odeini)
  
  for (ini.name in odeini.names) {
    # For DFOP metabolites in two compartments, need to calculate some initial conditions for the ODEs.
    if (!(ini.name %in% names(cake.model$diffs))){
      subcompartment1.name <- paste(ini.name, "1", sep="_")
      subcompartment2.name <- paste(ini.name, "2", sep="_")
      
      if (subcompartment1.name %in% names(cake.model$diffs) && subcompartment2.name %in% names(cake.model$diffs)){
        g.parameter.name = paste("g", ini.name, sep="_")
        
        odeini[[subcompartment1.name]] <- odeini[[ini.name]] * odeparms[[g.parameter.name]]   
        odeini[[subcompartment2.name]] <- odeini[[ini.name]] * (1 - odeparms[[g.parameter.name]])
      }
    }
  }
  
  # It is important that these parameters are stated in the same order as the differential equations.
  return(odeini[names(cake.model$diffs)])
}

# Post-processes the output from the ODE solver (or analytical process), including recombination of sub-compartments.
#
# odeoutput: The output of the ODE solver.
# cake.model: The expression of the model that we are solving.
# atol: The tolerance to which the solution has been calculated.
#
# Returns: Post-processed/transformed ODE output.
PostProcessOdeOutput <- function(odeoutput, cake.model, atol) {
  out_transformed <- data.frame(time = odeoutput[, "time"])
  
  # Replace values that are incalculably small with 0.
  for (col.name in colnames(odeoutput)) {
    if (col.name == "time") {
      next
    }
    
    # If we have non-NaN, positive outputs...
    if (length(odeoutput[, col.name][!is.nan(odeoutput[, col.name]) && odeoutput[, col.name] > 0]) > 0) {
      # ...then replace the NaN outputs.
      odeoutput[, col.name][is.nan(odeoutput[, col.name])] <- 0
    }
    
    # Round outputs smaller than the used tolerance down to 0.
    odeoutput[, col.name][odeoutput[, col.name] < atol] <- 0
  }
  
  # Re-combine sub-compartments (if required)
  for (compartment.name in names(cake.model$map)) {
    if (length(cake.model$map[[compartment.name]]) == 1) {
      out_transformed[compartment.name] <- odeoutput[, compartment.name]
    } else {
      out_transformed[compartment.name] <- rowSums(odeoutput[, cake.model$map[[compartment.name]]])
    }
  }
  
  return(out_transformed)
}

# Reorganises data in a wide format to a long format.
#
# wide_data: The data in wide format.
# time: The name of the time variable in wide_data (default "t").
#
# Returns: Reorganised data.
wide_to_long <- function(wide_data, time = "t") {
  colnames <- names(wide_data)
  
  if (!(time %in% colnames)) {
    stop("The data in wide format have to contain a variable named ", time, ".")
  }
  
  vars <- subset(colnames, colnames != time)
  n <- length(colnames) - 1
  long_data <- data.frame(name = rep(vars, each = length(wide_data[[time]])), 
                          time = as.numeric(rep(wide_data[[time]], n)), value = as.numeric(unlist(wide_data[vars])), 
                          row.names = NULL)
  
  return(long_data)
}

Contact - Imprint