Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1230 time offset #1232

Merged
merged 7 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions R/simulation-set.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,39 +55,48 @@ SimulationSet <- R6::R6Class(
# Test and validate the simulation objects
validateIsString(simulationSetName)
validateIsString(simulationFile)
validateIsString(massBalanceFile, nullAllowed = TRUE)
validateIsFileExtension(simulationFile, "pkml")
validateIsUnitFromDimension(timeUnit, "Time")

# For optional input, usually null is allowed
# but not here as it would mean that nothing would be reported
validateIsIncluded(c(applicationRanges), ApplicationRanges)
validateIsString(massBalanceFile, nullAllowed = TRUE)
if (!isEmpty(massBalanceFile)) {
validateIsFileExtension(massBalanceFile, "json")
self$massBalanceSettings <- jsonlite::fromJSON(massBalanceFile, simplifyVector = FALSE)[["MassBalancePlots"]]
}
validateIsString(timeUnit, nullAllowed = TRUE)
validateIsPositive(object = minimumSimulationEndTime, nullAllowed = TRUE)
validateIsNumeric(timeOffset)
# For optional input, usually null is allowed
# but not here as it would mean that nothing would be reported
validateIsIncluded(c(applicationRanges), ApplicationRanges)


# Before loading the simulation, check if the file exists
validateFileExists(simulationFile)
simulation <- ospsuite::loadSimulation(simulationFile, addToCache = FALSE)

validateVector(minimumSimulationEndTime, type = "numeric", valueRange = c(0, Inf), nullAllowed = TRUE)
# Following checks require simulation info
endTime <- max(
minimumSimulationEndTime,
ospsuite::toUnit(
quantityOrDimension = "Time",
values = simulation$outputSchema$endTime,
targetUnit = timeUnit
)
)
validateVectorRange(timeOffset, type = "numeric", valueRange = c(0, endTime))

# Test and validate outputs and their paths
validateOutputObject(c(outputs), simulation, nullAllowed = TRUE)
validateDataSource(dataSource, c(outputs), nullAllowed = TRUE)
# Warn if time offset is not in application times (or 0 if there is no application at all)
allApplicationTimes <- getApplicationTimesForSimulation(simulation, c(outputs))
checkIsIncluded(timeOffset, allApplicationTimes %||% 0, groupName = "Application Times")

self$simulationSetName <- simulationSetName
self$simulationFile <- simulationFile
self$minimumSimulationEndTime <- minimumSimulationEndTime
self$timeOffset <- timeOffset

self$outputs <- c(outputs)

self$dataSource <- dataSource
self$dataSelection <- translateDataSelection(dataSelection)

self$timeUnit <- timeUnit %||% "h"

self$timeUnit <- timeUnit
self$applicationRanges <- list(
total = isIncluded(ApplicationRanges$total, applicationRanges),
firstApplication = isIncluded(ApplicationRanges$firstApplication, applicationRanges),
Expand Down
35 changes: 10 additions & 25 deletions R/utilities-goodness-of-fit.R
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ plotPopTimeProfileLog <- function(simulatedData,
#' @return A data.frame
#' @keywords internal
asTimeAfterDose <- function(data, timeOffset, maxTime = NULL) {
if (isOfLength(data, 0)) {
if (isEmpty(data)) {
# Return empty data.frame (consitent class with data[dataFilter, ])
return(data.frame())
}
Expand Down Expand Up @@ -772,7 +772,8 @@ getMetaDataFrame <- function(listOfMetaData) {
#' @title getTimeProfilePlotResults
#' @description Get plots and their captions for mean or population time profiles
#' @param workflowType `"mean"` or `"population"` workflow
#' @param timeRange array of time values defining range of simulated data
#' @param timeRange array of time values defining range of simulated data.
#' Note that `timeRange` accounts for user defined `timeOffset`
#' @param simulatedData data.frame of simulated data
#' @param observedData data.frame of observed data
#' @param metaDataFrame metaData represented as a data.frame
Expand All @@ -789,51 +790,35 @@ getTimeProfilePlotResults <- function(workflowType, timeRange, simulatedData, ob
# rbind.data.frame enforces data.frame type and nrow can be used as is
# Reset time based on timeOffset or application range
simulatedData <- rbind.data.frame(
asTimeAfterDose(
settings$referenceData$simulatedData,
min(timeRange) + settings$referenceData$timeOffset,
max(timeRange) + settings$referenceData$timeOffset
),
asTimeAfterDose(
simulatedData,
min(timeRange) + structureSet$simulationSet$timeOffset,
max(timeRange) + structureSet$simulationSet$timeOffset
)
asTimeAfterDose(settings$referenceData$simulatedData, min(timeRange), max(timeRange)),
asTimeAfterDose(simulatedData, min(timeRange), max(timeRange))
)
if (nrow(simulatedData) == 0) {
stop(messages$dataIncludedInTimeRange(
nrow(simulatedData),
timeRange + structureSet$simulationSet$timeOffset,
timeRange,
structureSet$simulationSet$timeUnit, "simulated"
))
}
observedData <- asTimeAfterDose(
observedData,
min(timeRange) + structureSet$simulationSet$timeOffset,
max(timeRange) + structureSet$simulationSet$timeOffset
)
observedData <- asTimeAfterDose(observedData, min(timeRange), max(timeRange))
# Add reference observed data only if option is set to true
# isTRUE is used for mean model workflows that have a NULL field
if (isTRUE(structureSet$simulationSet$plotReferenceObsData)) {
observedData <- rbind.data.frame(
asTimeAfterDose(
settings$referenceData$observedData,
min(timeRange) + settings$referenceData$timeOffset,
max(timeRange) + settings$referenceData$timeOffset
),
asTimeAfterDose(settings$referenceData$observedData, min(timeRange), max(timeRange)),
observedData
)
}

logDebug(messages$dataIncludedInTimeRange(
nrow(simulatedData),
timeRange + structureSet$simulationSet$timeOffset,
timeRange,
structureSet$simulationSet$timeUnit,
"simulated"
))
logDebug(messages$dataIncludedInTimeRange(
nrow(observedData),
timeRange + structureSet$simulationSet$timeOffset,
timeRange,
structureSet$simulationSet$timeUnit,
"observed"
))
Expand Down
52 changes: 37 additions & 15 deletions R/utilities-simulation-set.R
Yuri05 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another question: is it guaranteed that the time point of the offset is included in the list of the output time points?
E.g. if my initial output raster is 0h, 1h, 2h, ... and my time offset is 30 min: will 30min be included in the output time points?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently do not update the output schema. So, the output time points won't necessarily include the offset

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question not related to the current PR: in the lines 132 and 136, minimumSimulationEndTime is assumed to be in minutes

if (simulationSet$minimumSimulationEndTime > simulation$outputSchema$endTime) {
maximalIntervalIndex <- which(sapply(simulation$outputSchema$intervals, function(x) {
x$endTime$value
}) == simulation$outputSchema$endTime)[1]
simulation$outputSchema$intervals[[maximalIntervalIndex]]$endTime$setValue(value = simulationSet$minimumSimulationEndTime, unit = ospUnits$Time$min)

However when passed to the simulation set constructor, minimumSimulationEndTime is assumed to be in the user defined unit timeUnit (which is also [h] as per default).

SimulationSet <- R6::R6Class(
"SimulationSet",
public = list(
simulationSetName = NULL,
simulationFile = NULL,
outputs = NULL,
dataSource = NULL,
dataSelection = NULL,
timeUnit = NULL,
applicationRanges = NULL,
minimumSimulationEndTime = NULL,
timeOffset = NULL,
massBalanceSettings = NULL,

As long as there is no unit conversion when setting minimumSimulationEndTime it seems to be a bug?
@pchelle

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. minimumSimulationEndTime should be defined by user in timeUnit, and be converted in baseUnit in this function.
I created issue #1238 to fix this

Original file line number Diff line number Diff line change
Expand Up @@ -183,26 +183,18 @@ getSimulationTimeRanges <- function(simulation, path, simulationSet) {
)
)

# Get applications
applications <- simulation$allApplicationsFor(path)
applicationTimes <- 0
if (!isOfLength(applications, 0)) {
applicationTimes <- sapply(applications, function(application) {
application$startTime$value
})
}
# Get applications times
applicationTimes <- getApplicationTimesForPath(simulation, path) %||% 0
# Get all ranges of simulation ranked defined by application intervals
simulationRanges <- c(applicationTimes, simulation$outputSchema$endTime)
simulationRanges <- sort(ospsuite::toUnit("Time", simulationRanges, timeUnit))

# Store number of applications and their ranges
logDebug(messages$numberOfApplications(length(applications), path, simulation$name))
logDebug(messages$numberOfApplications(length(applicationTimes), path, simulation$name))
logDebug(messages$timeRangesForSimulation(paste0(simulationRanges, collapse = "', '"), simulation$name))

# Define ranges for output
# Depending on expected behaviour of settings$applicationRange
# It would be possible to set these values
timeRanges$total$values <- c(min(simulationRanges), max(simulationRanges))
# Define ranges for output based on time offset and simulation
timeRanges$total$values <- c(simulationSet$timeOffset, max(simulationRanges))
Yuri05 marked this conversation as resolved.
Show resolved Hide resolved

# Flag simulationRanges prior to timeOffset
timeOffsetFlag <- simulationRanges < simulationSet$timeOffset
Expand All @@ -217,12 +209,42 @@ getSimulationTimeRanges <- function(simulation, path, simulationSet) {

# Case of multiple applications, get first and last
if (!isOfLength(simulationRanges, 2)) {
# First application becomes first application after timeOffset
# First application becomes first application since timeOffset
timeRanges$firstApplication$values <- utils::head(simulationRanges[!timeOffsetFlag], 2)
timeRanges$lastApplication$values <- utils::tail(simulationRanges, 2)
# Last application becomes last application since timeOffset
timeRanges$lastApplication$values <- utils::tail(simulationRanges[!timeOffsetFlag], 2)
timeRanges$firstApplication$keep <- applicationRanges[[ApplicationRanges$firstApplication]]
timeRanges$lastApplication$keep <- applicationRanges[[ApplicationRanges$lastApplication]]
}

return(timeRanges)
}

#' @title getApplicationTimesForSimulation
#' @param simulation A `Simulation` object
#' @param output A list of `Output` objects
#' @return Time values of applications
#' @keywords internal
getApplicationTimesForSimulation <- function(simulation, outputs){
allApplicationsTimes <- lapply(
outputs,
function(output) {
getApplicationTimesForPath(simulation, output$path)
}
)
# Concatenate all the application times in one single numeric vector
return(do.call("c", allApplicationsTimes))
}

#' @title getApplicationTimesForPath
#' @param simulation A `Simulation` object
#' @param path A simulation path
#' @return Time values of applications
#' @import ospsuite
#' @keywords internal
getApplicationTimesForPath <- function(simulation, path){
applications <- simulation$allApplicationsFor(path)
if (isEmpty(applications)) {return()}
applicationTimes <- sapply(applications, function(application) {application$startTime$value})
return(as.numeric(applicationTimes))
}
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ init:
install:
- ps: Bootstrap
- git submodule update --init --recursive
- ps: cinst pandoc --no-progress
- ps: choco install pandoc --no-progress
- ps: $env:Path += ";C:\Program Files (x86)\Pandoc\"

environment:
Expand Down
2 changes: 1 addition & 1 deletion man/CalculatePKParametersTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/GofPlotTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/GofTaskSettings.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/MeanModelWorkflow.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/PlotTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/PopulationPlotTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/PopulationSensitivityAnalysisTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/PopulationSimulationSet.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/PopulationWorkflow.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/QualificationTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/QualificationWorkflow.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/SensitivityAnalysisTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/SimulationTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/checkSamePopulationIds.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions man/getApplicationTimesForPath.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions man/getApplicationTimesForSimulation.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading