-
Hi, I am reaching out to seek your guidance on an issue I've encountered with my simulation results. I've been using individual parameters as inputs for my simulations, but I've consistently obtained negative values, which theoretically shouldn't occur. Despite my efforts to ensure that the CENT compartment remains non-negative through the simulation code, the issue persists. Thanks in advance, $PROB Model:
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 1 reply
-
Hi @XuefenYin - Thanks for your question. There are some situations when the amount in Can you share some code that executes a simulation where For example library(mrgsolve)
#>
#> Attaching package: 'mrgsolve'
#> The following object is masked from 'package:stats':
#>
#> filter
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
code <- '
$PARAM @annotated
KA : 0.02 : Absorption rate constant 1 (1/hour)
CL : 50 : Clearance (L/hour)
V2 : 347 : Central volume (L)
Q : 10 : Inter-compartmental clearance (L/hour)
V3 : 70 : Peripheral volume of distribution (L)
ALAG1: 1.5 : Lag time for KA1 (hour)
TR : 0.02: Logit transformation factor for F1
$CMT @annotated
GUT : Dosing compartment (mg)
CENT : Central compartment (mg)
PERIPH : Peripheral compartment (mg)
AUC : AUC (mg*hour/L)
$GLOBAL // visit one time
#define S2 (V2/1000)
#define CP (CENT/S2)
$MAIN // visit every time
double K = CL/V2;
double K23 = Q/V2;
double K32 = Q/V3;
double F1 = 1 - (TR/(1+TR));
ALAG_GUT = ALAG1;
$ODE
double CENT2 = std::max(0.0, CENT);
dxdt_GUT = -KA*GUT;
dxdt_CENT = KA*GUT - K23*CENT2 + K32*PERIPH - K*CENT2;
dxdt_PERIPH = K23*CENT2 - K32*PERIPH;
dxdt_AUC = CP;
$CAPTURE @annotated
CP : Plasma concentration (mass/time)
'
mod <- mcode("foo", code)
#> Building foo ...
#> done.
mrgsim(mod, ev(amt = 1)) %>% summary()
#> ID time GUT CENT
#> Min. :1 Min. : 0.00 Min. :0.0000 Min. :0.00000
#> 1st Qu.:1 1st Qu.: 5.25 1st Qu.:0.6805 1st Qu.:0.05324
#> Median :1 Median :11.50 Median :0.7711 Median :0.08789
#> Mean :1 Mean :11.54 Mean :0.7091 Mean :0.06907
#> 3rd Qu.:1 3rd Qu.:17.75 3rd Qu.:0.8737 3rd Qu.:0.09420
#> Max. :1 Max. :24.00 Max. :0.9900 Max. :0.09541
#> PERIPH AUC CP
#> Min. :0.000000 Min. :0.0000 Min. :0.0000
#> 1st Qu.:0.002706 1st Qu.:0.3256 1st Qu.:0.1534
#> Median :0.010235 Median :1.6621 Median :0.2533
#> Mean :0.009187 Mean :1.9459 Mean :0.1991
#> 3rd Qu.:0.015320 3rd Qu.:3.3353 3rd Qu.:0.2715
#> Max. :0.017524 Max. :5.0394 Max. :0.2750 Created on 2024-03-06 with reprex v2.0.2 This doesn't have negative values in the output. But your simulation would have it. |
Beta Was this translation helpful? Give feedback.
-
@kylebaron Thanks so much for the quick reply. I attached the simulation code below, which I get the negative CENT and CP. I also attached the idata.10ID.csv file, which contains the dosing record and individual parameters. I greatly appreciate your help with this matter. Best regard, input datacode starts belowlibrary(tidyverse) code <- ' $CMT @Annotated $GLOBAL // visit one time #define S2 (V2/1000) $MAIN // visit every time double K = CL/V2; ALAG_GUT = ALAG1; $ODE double CENT2 = std::max(0.0, CENT); dxdt_GUT = -KAGUT; $CAPTURE @Annotated mod <- mcode("foo", code) idata.10ID<- read.csv("idata.10ID.csv") out <- mrgsim_df(mod, summary(out) Save the negative CPCP_NEGATIVE <- filter(out, CP < 0) Check if CP_NEGATIVE dataframe has any rowsif (nrow(CP_NEGATIVE) > 0) { negative_indices <- which(out$CP < 0) the first 10 rows of CP_NEGATIVE for verificationprint("First 10 rows with negative CP values:") print("There are no negative numbers in the dataset.") |
Beta Was this translation helpful? Give feedback.
-
Hi @XuefenYin - Please see the output below. Those negative numbers are happening after essentially all the mass has left the system. The ode solver can only give so much precision in that case and those numbers are just error around zero. A couple of things you can try
You can read more about this on this wkii page: https://github.com/metrumresearchgroup/mrgsolve/wiki/Controlling-unphysical-negative-values which I lifted from sundials documentation; it talks about these unphysiological negative values. This other page on setting tolerances would be relevant too: https://github.com/metrumresearchgroup/mrgsolve/wiki/General-advice-on-choice-of-tolerances Hope this helps! library(mrgsolve)
#>
#> Attaching package: 'mrgsolve'
#> The following object is masked from 'package:stats':
#>
#> filter
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
code <- '
$PARAM @annotated
KA : 0.02 : Absorption rate constant 1 (1/hour)
CL : 50 : Clearance (L/hour)
V2 : 347 : Central volume (L)
Q : 10 : Inter-compartmental clearance (L/hour)
V3 : 70 : Peripheral volume of distribution (L)
ALAG1: 1.5 : Lag time for KA1 (hour)
TR : 0.02: Logit transformation factor for F1
$CMT @annotated
GUT : Dosing compartment (mg)
CENT : Central compartment (mg)
PERIPH : Peripheral compartment (mg)
AUC : AUC (mg*hour/L)
$GLOBAL // visit one time
#define S2 (V2/1000)
#define CP (CENT/S2)
$MAIN // visit every time
double K = CL/V2;
double K23 = Q/V2;
double K32 = Q/V3;
double F1 = 1 - (TR/(1+TR));
ALAG_GUT = ALAG1;
$ODE
double CENT2 = std::max(0.0, CENT);
dxdt_GUT = -KA*GUT;
dxdt_CENT = KA*GUT - K23*CENT2 + K32*PERIPH - K*CENT2;
dxdt_PERIPH = K23*CENT2 - K32*PERIPH;
dxdt_AUC = CP;
$CAPTURE @annotated
CP : Plasma concentration (mass/time)
'
mod <- mcode("foo", code)
#> Building foo ...
#> done.
idata <- read.csv("/Users/kyleb/helpr/idata.10ID.csv")
out <- mrgsim(mod,
idata,
recover = c("CL"),
carry.out = "EVID",
recsort = 3,
atol = 1e-12,
rtol = 1e-10,
tgrid = seq(0, 25000, by = 1))
summary(out)
#> ID TIME EVID GUT
#> Min. : 1.000 Min. : 0 Min. :0.000000 Min. : 0.000
#> 1st Qu.: 3.000 1st Qu.: 6205 1st Qu.:0.000000 1st Qu.: 0.000
#> Median : 6.000 Median :12470 Median :0.000000 Median : 0.000
#> Mean : 5.501 Mean :12474 Mean :0.002358 Mean : 4.157
#> 3rd Qu.: 8.000 3rd Qu.:18735 3rd Qu.:0.000000 3rd Qu.: 0.000
#> Max. :10.000 Max. :25000 Max. :1.000000 Max. :132.639
#> CENT PERIPH AUC CP
#> Min. : 0.0000 Min. :0.0000 Min. : 0 Min. : 0.000
#> 1st Qu.: 0.0000 1st Qu.:0.0000 1st Qu.: 22910 1st Qu.: 0.000
#> Median : 0.0000 Median :0.0000 Median : 39829 Median : 0.000
#> Mean : 0.9633 Mean :0.2149 Mean : 70284 Mean : 3.065
#> 3rd Qu.: 0.0000 3rd Qu.:0.0000 3rd Qu.: 87350 3rd Qu.: 0.000
#> Max. :42.5620 Max. :8.4453 Max. :195449 Max. :122.849
#> CL
#> Min. :21.06
#> 1st Qu.:34.57
#> Median :39.11
#> Mean :37.91
#> 3rd Qu.:44.19
#> Max. :56.12
plot(out) CP_NEGATIVE <- filter(out, CP < 0)
if (nrow(CP_NEGATIVE) > 0) {
print(paste("There are", nrow(CP_NEGATIVE), "negative numbers in the dataset."))
negative_indices <- which(out$CP < 0)
print("First 10 rows with negative CP values:")
print(head(CP_NEGATIVE, 10))
} else {
print("There are no negative numbers in the dataset.")
}
#> [1] "There are 214825 negative numbers in the dataset."
#> [1] "First 10 rows with negative CP values:"
#> # A tibble: 10 × 9
#> ID TIME EVID GUT CENT PERIPH AUC CP CL
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1 5727 0 3.47e-18 -3.15e-17 6.18e-18 87350. -9.06e-17 21.1
#> 2 1 5728 0 3.47e-18 -1.93e-16 6.52e-18 87350. -5.56e-16 21.1
#> 3 1 5729 0 3.47e-18 -3.54e-16 6.86e-18 87350. -1.02e-15 21.1
#> 4 1 5730 0 3.47e-18 -5.16e-16 7.20e-18 87350. -1.49e-15 21.1
#> 5 1 5731 0 3.47e-18 -6.77e-16 7.54e-18 87350. -1.95e-15 21.1
#> 6 1 5732 0 3.46e-18 -8.39e-16 7.88e-18 87350. -2.42e-15 21.1
#> 7 1 5733 0 3.46e-18 -1.00e-15 8.22e-18 87350. -2.88e-15 21.1
#> 8 1 5734 0 3.46e-18 -1.16e-15 8.56e-18 87350. -3.35e-15 21.1
#> 9 1 5735 0 3.46e-18 -1.32e-15 8.90e-18 87350. -3.81e-15 21.1
#> 10 1 5736 0 3.45e-18 -1.48e-15 9.24e-18 87350. -4.28e-15 21.1 Created on 2024-03-06 with reprex v2.0.2 |
Beta Was this translation helpful? Give feedback.
-
@kylebaron Thank you for your clear explanation and suggested solutions. Your expertise has resolved my concerns regarding simulation inaccuracies. Considering the requirement to cover all patient time points, I am opting for Solution 3. Your guidance is greatly appreciated. |
Beta Was this translation helpful? Give feedback.
Hi @XuefenYin -
Please see the output below. Those negative numbers are happening after essentially all the mass has left the system. The ode solver can only give so much precision in that case and those numbers are just error around zero.
A couple of things you can try
atol
smaller (like1e-10
or1e-20
or something like that; that will give you additional precision around those numbers and they might stay small and positive; this didn't work for me to get rid of all the negative valuesYou can read more about this on this wkii page: https://github.com/metrumresearchgroup/mrgso…