diff --git a/.Rbuildignore b/.Rbuildignore index 5b7c9bddb..0415ac81f 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,6 +1,6 @@ ^.*\.Rproj$ ^\.Rproj\.user$ -^appveyor\.yml$ +(^appveyor)(.*)(.yml$) ^README\.md$ ^tests/dev ^tools @@ -20,7 +20,6 @@ ^doc$ ^docs$ ^_pkgdown\.yml$ -^appveyor\.yml$ ^.gitlab-ci\.yml$ ^data-raw$ ^pkgdown$ diff --git a/.covrignore b/.covrignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/DESCRIPTION b/DESCRIPTION index c52fb372e..70646ea8a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: ospsuite Title: R package to manipulate OSPSuite Models -Version: 11.0.0 +Version: 11.1.0 Authors@R: c(person("Open-Systems-Pharmacology Community", role = "cph"), person("Michael", "Sevestre", role = c("aut", "cre"), email = "michael@design2code.ca"), @@ -12,7 +12,9 @@ Authors@R: Description: The ospsuite-R package provides the functionality of loading, manipulating, and simulating the simulations created in the Open Systems Pharmacology Software tools PK-Sim and MoBi. License: GPL-2 | file LICENSE -URL: https://github.com/open-systems-pharmacology/ospsuite-r +URL: https://github.com/open-systems-pharmacology/ospsuite-r, + https://www.open-systems-pharmacology.org/OSPSuite-R/ (release), + https://www.open-systems-pharmacology.org/OSPSuite-R/dev (development) BugReports: https://github.com/open-systems-pharmacology/ospsuite-r/issues Depends: @@ -20,21 +22,24 @@ Depends: rClr (>= 0.9.1) Imports: dplyr (>= 1.0.0), - ospsuite.utils (>= 1.3.0), + ospsuite.utils (>= 1.4.0), purrr, - R6, readr, stringr, tidyr, - tlf (>= 1.3.0) + ggplot2, + rlang, + tlf (>= 1.4.0) Suggests: knitr, rmarkdown, testthat (>= 3.0.3), - vdiffr (>= 1.0.0) + vdiffr (>= 1.0.0), + withr +Language: en-US Encoding: UTF-8 LazyData: true -RoxygenNote: 7.2.0 +RoxygenNote: 7.2.3 Roxygen: list(markdown = TRUE) VignetteBuilder: knitr Collate: @@ -66,12 +71,14 @@ Collate: 'output-selections.R' 'parameter-range.R' 'parameter.R' - 'path-explorer.R' 'pk-parameter-sensitivity.R' 'pk-parameter.R' 'pk-sim.R' 'plot-individual-time-profile.R' + 'plot-observed-vs-simulated.R' 'plot-population-time-profile.R' + 'plot-residuals-vs-simulated.R' + 'plot-residuals-vs-time.R' 'population-characteristics.R' 'population.R' 'quantity-pk-parameter.R' diff --git a/NAMESPACE b/NAMESPACE index 179b2316d..644644829 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -34,9 +34,11 @@ export(addUserDefinedPKParameter) export(allAvailableDimensions) export(allPKParameterNames) export(calculatePKAnalyses) +export(calculateResiduals) export(clearMemory) export(clearOutputIntervals) export(clearOutputs) +export(convertUnits) export(createDistributions) export(createImporterConfigurationForFile) export(createIndividual) @@ -69,6 +71,7 @@ export(getAllParametersForSensitivityAnalysisMatching) export(getAllParametersMatching) export(getAllQuantitiesMatching) export(getAllQuantityPathsIn) +export(getAllStateVariableParametersPaths) export(getAllStateVariablesPaths) export(getBaseUnit) export(getContainer) @@ -80,6 +83,7 @@ export(getOutputValues) export(getParameter) export(getParameterDisplayPaths) export(getQuantity) +export(getQuantityValuesByPath) export(getSimulationTree) export(getStandardMoleculeParameters) export(getUnitsForDimension) @@ -89,6 +93,7 @@ export(importPKAnalysesFromCSV) export(importResultsFromCSV) export(importSensitivityAnalysisResultsFromCSV) export(initPKSim) +export(isExplicitFormulaByPath) export(loadAgingDataFromCSV) export(loadDataImporterConfiguration) export(loadDataSetFromPKML) @@ -104,7 +109,10 @@ export(pkAnalysesToTibble) export(pkParameterByName) export(plotGrid) export(plotIndividualTimeProfile) +export(plotObservedVsSimulated) export(plotPopulationTimeProfile) +export(plotResidualsVsSimulated) +export(plotResidualsVsTime) export(populationAsDataFrame) export(populationToDataFrame) export(populationToTibble) diff --git a/NEWS.md b/NEWS.md index 73e21d5c1..636605b32 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,39 @@ -# ospsuite 11.0 (development version) +# ospsuite 11.1.197 + +## New features + +* Adds new visualization functions: + + - `plotObservedVsSimulated()` for observed versus simulated data scatter plot. + - `plotResidualsVsTime()` for time versus residuals data scatter plot. + - `plotResidualsVsSimulated()` for simulated versus residuals data scatter plot. + +* Adds new helper functions to work with `DataCombined` objects: + + - `convertUnits()` to convert datasets in `DataCombined` to common units. + - `calculateResiduals()` to calculate residuals for datasets in `DataCombined`. + +## Major Changes + +* The class `SimulationBatch` gets a new property `id`. + +* The output of `runSimulationBatches()` is now a named list with names being + the ids of `SimulationBatch`. + +* `calculateResiduals()` now uses `log(base = 10)` for calculation of residuals +in logarithmic scale instead if `log(base = exp(1))` +* `calculateResiduals()` does also return residuals for entries where simulated +or observed value is 0 in logarithmic scale. These values were ignored in previous +versions. If the observed or simulated value is zero or negative, it is replaced +by an arbitrary small value `getOSPSuiteSetting("LOG_SAFE_EPSILON")` (1e-20 by default). + +## Minor Changes + +* `SimulationBatch$addRunValues()` will throw an error when any start value is `NaN` +* `SimulatioBatch` gets methods `getVariableParameters()` and `getVariableMolecules()` +that return list of parameter resp. molecule paths that are defined variable. + +# ospsuite 11.0.123 ## New features diff --git a/R/cache.R b/R/cache.R index 5962d3ebd..7ad9e75ab 100644 --- a/R/cache.R +++ b/R/cache.R @@ -94,7 +94,7 @@ Cache <- R6::R6Class( if (missing(value)) { ls(private$cachedObjects) } else { - stop(messages$errorPropertyReadOnly("keys"), call. = FALSE) + stop(messages$errorPropertyReadOnly("keys")) } } ), diff --git a/R/container.R b/R/container.R index 68040e83c..2798bc259 100644 --- a/R/container.R +++ b/R/container.R @@ -2,6 +2,7 @@ #' @docType class #' @description Contains other entities such as Parameter or containers #' @format NULL +#' @keywords internal Container <- R6::R6Class( "Container", cloneable = FALSE, diff --git a/R/data-combined.R b/R/data-combined.R index 15cd77e1f..b07823b41 100644 --- a/R/data-combined.R +++ b/R/data-combined.R @@ -14,33 +14,61 @@ #' @import tidyr #' @import ospsuite.utils #' -#' @param groups A string or a list of strings assigning the data set to a -#' group. If an entry within the list is `NULL`, the corresponding data set is -#' not assigned to any group (and the corresponding entry in the `group` -#' column will be an `NA`). If provided, `groups` must have the same length as -#' `dataSets` and/or `simulationResults$quantityPath`. If no grouping is -#' specified for any of the dataset, the column `group` in the data frame -#' output will be all `NA`. +#' @param names A string or a `list` of strings assigning new names. These new +#' names can be either for renaming `DataSet` objects, or for renaming +#' quantities/paths in `SimulationResults` object. If an entity is not to be +#' renamed, this can be specified as `NULL`. E.g., in `names = list("oldName1" +#' = "newName1", "oldName2" = NULL)`), dataset with name `"oldName2"` will not +#' be renamed. The list can either be named or unnamed. Names act as unique +#' identifiers for data sets in the `DataCombined` object and, therefore, +#' duplicate names are not allowed. +#' @param groups A string or a list of strings specifying group name +#' corresponding to each data set. If an entry within the list is `NULL`, the +#' corresponding data set is not assigned to any group (and the corresponding +#' entry in the `group` column will be an `NA`). If provided, `groups` must +#' have the same length as `dataSets` and/or `simulationResults$quantityPath`. +#' If no grouping is specified for any of the dataset, the column `group` in +#' the data frame output will be all `NA`. #' #' @examples -#' -#' # load the simulation +#' # simulated data #' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") #' sim <- loadSimulation(simFilePath) -#' simulationResults <- runSimulation(simulation = sim) +#' simResults <- runSimulation(sim) +#' outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)" #' -#' # create a new dataset object -#' dataSet <- DataSet$new(name = "DS") +#' # observed data +#' obsData <- lapply( +#' c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"), +#' function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite")) +#' ) +#' names(obsData) <- lapply(obsData, function(x) x$name) #' -#' # created object with datasets combined -#' myCombDat <- DataCombined$new() -#' myCombDat$addSimulationResults(simulationResults) -#' myCombDat$addDataSets(dataSet) #' -#' # print the object -#' myCombDat -#' @docType class +#' # Create a new instance of `DataCombined` class +#' myDataCombined <- DataCombined$new() +#' +#' # Add simulated results +#' myDataCombined$addSimulationResults( +#' simulationResults = simResults, +#' quantitiesOrPaths = outputPath, +#' groups = "Aciclovir PVB" +#' ) +#' +#' # Add observed data set +#' myDataCombined$addDataSets(obsData$`Vergin 1995.Iv`, groups = "Aciclovir PVB") +#' +#' # Looking at group mappings +#' myDataCombined$groupMap +#' +#' # Looking at the applied transformations +#' myDataCombined$dataTransformations #' +#' # Accessing the combined data frame +#' myDataCombined$toDataFrame() +#' +#' @family data-combined +#' @docType class #' @export DataCombined <- R6::R6Class( classname = "DataCombined", @@ -50,13 +78,8 @@ DataCombined <- R6::R6Class( public = list( - #' @param dataSets Instance (or a `list` of instances) of the `DataSet` + #' @param dataSets An instance (or a `list` of instances) of the `DataSet` #' class. - #' @param names A string or a list of strings assigning new names to the - #' list of instances of the `DataSet` class. If a dataset is not to be - #' renamed, this can be specified as `NULL` in the list. For example, in - #' `names = list("dataName" = "dataNewName", "dataName2" = NULL)`), - #' dataset with name `"dataName2"` will not be renamed. #' #' @description #' Adds observed data. @@ -65,11 +88,13 @@ DataCombined <- R6::R6Class( addDataSets = function(dataSets, names = NULL, groups = NULL) { # Validate vector arguments' type and length validateIsOfType(dataSets, "DataSet", FALSE) - names <- .cleanVectorArgs(names, objectCount(dataSets), type = "character") + numberOfDatasets <- objectCount(dataSets) + names <- .cleanVectorArgs(names, numberOfDatasets, type = "character") - # The original names for datasets can be "plucked" from respective - # objects. `purrr::map()` is used to iterate over the vector and the - # anonymous function is used to pluck an object. The `map_chr()` variant + # The original names for datasets can be "plucked" from objects. + # + # `purrr::map()` iterates over the vector and applies the anonymous + # function to pluck name from the object. The `map_chr()` variant # clarifies that we are always expecting a character type in return. datasetNames <- purrr::map_chr(c(dataSets), function(x) purrr::pluck(x, "name")) @@ -104,14 +129,15 @@ DataCombined <- R6::R6Class( # from `ospsuite::getOutputValues()` to avoid repetition. #' @param simulationResults Object of type `SimulationResults` produced by - #' calling `runSimulation()` on a `Simulation` object. + #' calling `runSimulation()` on a `Simulation` object. Only a single + #' instance is allowed in a given `$addSimulationResults()` method call. #' @param quantitiesOrPaths Quantity instances (element or list) typically - #' retrieved using `getAllQuantitiesMatching()` or quantity path (element or - #' list of strings) for which the results are to be returned. (optional) - #' When providing the paths, only absolute full paths are supported (i.e., - #' no matching with '*' possible). If `quantitiesOrPaths` is `NULL` - #' (default value), returns the results for all output defined in the - #' results. + #' retrieved using `getAllQuantitiesMatching()` or quantity path (element + #' or list of strings) for which the results are to be returned. + #' (optional) When providing the paths, only absolute full paths are + #' supported (i.e., no matching with '*' possible). If `quantitiesOrPaths` + #' is `NULL` (default value), returns the results for all output defined + #' in the results. #' @param individualIds Numeric IDs of individuals for which the results #' should be extracted. By default, all individuals from the results are #' considered. If the individual with the provided ID is not found, the ID @@ -119,12 +145,6 @@ DataCombined <- R6::R6Class( #' @param population Population used to calculate the `simulationResults` #' (optional). This is used only to add the population covariates to the #' resulting data frame. - #' @param names A string or a list of strings assigning new names to the - #' quantities or paths present in the entered `SimulationResults` object. - #' If a dataset is not to be renamed, this can be specified as `NULL` in - #' the list. For example, in `names = list("dataName" = "dataNewName", - #' "dataName2" = NULL)`), dataset with name `"dataName2"` will not be - #' renamed. #' #' @description #' @@ -137,10 +157,10 @@ DataCombined <- R6::R6Class( individualIds = NULL, names = NULL, groups = NULL) { - # validate vector arguments' type and length + # Validate vector arguments' type and length validateIsOfType(simulationResults, "SimulationResults", FALSE) - # A list or a vector of `SimulationResults` class instances is not allowed. + # A vector of `SimulationResults` class instances is not allowed. Why? # # If this were to be allowed, `quantitiesOrPaths`, `population`, and # `individualIds ` could all be different for every `SimulationResults` @@ -154,7 +174,7 @@ DataCombined <- R6::R6Class( pathsNames <- quantitiesOrPaths %||% simulationResults$allQuantityPaths pathsLength <- length(pathsNames) - # validate alternative names for their length and type + # Validate alternative names for their length and type names <- .cleanVectorArgs(names, pathsLength, type = "character") # If alternate names are provided for datasets, use them instead. @@ -172,9 +192,9 @@ DataCombined <- R6::R6Class( private$.simResultsToDataFrame( simulationResults = simulationResults, quantitiesOrPaths = quantitiesOrPaths, - population = population, - individualIds = individualIds, - names = names + population = population, + individualIds = individualIds, + names = names ) ) @@ -200,15 +220,15 @@ DataCombined <- R6::R6Class( #' @param groups A list specifying which datasets belong to which group(s). #' Please note that the order in which groups are specified should match #' the order in which datasets were specified for `names` parameter. For - #' example, if datsets are named `"x"`, `"y"`, `"z"`, and the desired - #' groupings for them are, respectively, `"a"`, `"b"`, and no grouping, - #' this can be specified as `names = list("x", "y"), groups = list("a", - #' "b")`. Datasets for which no grouping is to be specified, can be left - #' out of the `groups` argument. The column `group` in the data frame - #' output will be `NA` for such datasets. If you wish to remove *existing* - #' grouping assignment for a given dataset, you can specify it as - #' following: `list("x" = NA)` or `list("x" = NULL)`. This will not change - #' any of the other (previously specified) groupings. + #' example, if data sets are named `"x"`, `"y"`, `"z"`, and the desired + #' groupings for them are, respectively, `"a"`, `"b"`, this can be + #' specified as `names = list("x", "y"), groups = list("a", "b")`. + #' Datasets for which no grouping is to be specified, can be left out of + #' the `groups` argument. The column `group` in the data frame output will + #' be `NA` for such datasets. If you wish to remove an *existing* grouping + #' assignment for a given dataset, you can specify it as following: + #' `list("x" = NA)` or `list("x" = NULL)`. This will not change any of the + #' other groupings. #' #' @description #' Adds grouping information to (observed and/or simulated) datasets. @@ -223,14 +243,26 @@ DataCombined <- R6::R6Class( # Sanitize vector arguments of `character` type names <- .cleanVectorArgs(names, type = "character") groups <- .cleanVectorArgs(groups, type = "character") - validateIsSameLength(names, groups) + + # `names` and `groups` need to be of the same length only if each dataset + # is assigned to a different group. But it is possible that the users + # want to assign all entered datasets to the same group. + # + # In the latter case, `groups` argument can be a scalar (length 1, i.e.) + # and we don't need to check that names and groups are of the same length. + if (length(groups) > 1L) { + validateIsSameLength(names, groups) + } + + # All entered datasets should be unique, name being their identifier. validateHasOnlyDistinctValues(names) # Extract groupings and dataset names in a data frame. # # `purrr::simplify()` will simplify input vector (which can be an atomic - # vector or a list) to an atomic vector, and covers both of these + # vector or a list) to an atomic vector. That is, it'll cover both of these # contexts: + # # - `names/groups = c(...)` # - `names/groups = list(...)` groupData <- dplyr::tibble( @@ -266,7 +298,7 @@ DataCombined <- R6::R6Class( validateHasOnlyDistinctValues(names) # Extract dataset names in a data frame. Groupings for all of them are - # going to be `NA`, so make avail of tibble's recycling rule. + # going to be `NA`, so make avail of `{tibble}`'s recycling rule. groupData <- dplyr::tibble( name = purrr::simplify(names), group = NA_character_ @@ -291,7 +323,11 @@ DataCombined <- R6::R6Class( #' numeric value or a list of numeric values specifying offsets and #' scale factors to apply to raw values. The default offset is `0`, while #' default scale factor is `1`, i.e., the data will not be modified. If a - #' list is specified, it should be the same length as `names` argument. + #' list is specified, it should be the same length as `forNames` argument. + #' @param reset IF `TRUE`, only data transformations that are specified will + #' be retained. Not specified transformations will be reset to their defaults. + #' Default behavior is `FALSE`, e.g., setting only `xOffsets` will not reset + #' `xScaleFactors` if those have been set previously. #' #' @details #' @@ -307,24 +343,43 @@ DataCombined <- R6::R6Class( xOffsets = 0, yOffsets = 0, xScaleFactors = 1, - yScaleFactors = 1) { - + yScaleFactors = 1, + reset = FALSE) { # Check that the arguments to parameters make sense - xOffsets <- .cleanVectorArgs(xOffsets, type = "numeric") - yOffsets <- .cleanVectorArgs(yOffsets, type = "numeric") - xScaleFactors <- .cleanVectorArgs(xScaleFactors, type = "numeric") - yScaleFactors <- .cleanVectorArgs(yScaleFactors, type = "numeric") + xOffsetsNew <- .cleanVectorArgs(xOffsets, type = "numeric") + yOffsetsNew <- .cleanVectorArgs(yOffsets, type = "numeric") + xScaleFactorsNew <- .cleanVectorArgs(xScaleFactors, type = "numeric") + yScaleFactorsNew <- .cleanVectorArgs(yScaleFactors, type = "numeric") forNames <- .cleanVectorArgs(forNames, type = "character") + # If any of the values is missing, they are retained from already existing values + if ((!reset) & (!is.null(private$.dataTransformations))) { + if (any(!missing(xOffsets), !missing(yOffsets), !missing(xScaleFactors), !missing(yScaleFactors))) { + if (missing(xOffsets)) { + xOffsetsNew <- private$.dataTransformations$xOffsets + } + if (missing(yOffsets)) { + yOffsetsNew <- private$.dataTransformations$yOffsets + } + if (missing(xScaleFactors)) { + xScaleFactorsNew <- private$.dataTransformations$xScaleFactors + } + if (missing(yScaleFactors)) { + yScaleFactorsNew <- private$.dataTransformations$yScaleFactors + } + } + } + + # Apply specified data transformations private$.dataCombined <- private$.dataTransform( data = private$.dataCombined, forNames = forNames, - xOffsets = xOffsets, - yOffsets = yOffsets, - xScaleFactors = xScaleFactors, - yScaleFactors = yScaleFactors + xOffsets = xOffsetsNew, + yOffsets = yOffsetsNew, + xScaleFactors = xScaleFactorsNew, + yScaleFactors = yScaleFactorsNew ) # Update private field with transformation values @@ -345,7 +400,7 @@ DataCombined <- R6::R6Class( #' #' @return #' - #' In the returned data frame, the following columns will always be present: + #' In the returned tibble data frame, the following columns will always be present: #' #' name - group - dataType - xValues - xDimension - xUnit - yValues - #' yErrorValues - yDimension - yUnit - yErrorType - yErrorUnit - molWeight @@ -375,7 +430,8 @@ DataCombined <- R6::R6Class( #' @description #' Print the object to the console. print = function() { - # group map contains names and nature of the datasets and grouping details + # Group map contains names, types, and groupings for all datasets, providing + # the most succinct snapshot of the object. private$printClass() private$printLine("Datasets and groupings", addTab = FALSE) cat("\n") @@ -527,13 +583,13 @@ DataCombined <- R6::R6Class( # If the newly entered dataset(s) are already present, then replace the # existing ones with the new ones. # - # For example, someone can all `$addSimulationResults(dataSet1)` and - # then again call `$addSimulationResults(dataSet1)` with the same class + # For example, someone can all `$addDataSets(dataSet1)` and + # then again call `$addDataSets(dataSet1)` with the same class # instance because they realized that the first time they created the - # DataSet object, they had made a mistake. In this case, data frame + # `DataSet` object, they had made a mistake. In this case, data frame # created in the latter call should replace the one created in the # former call. If we were not to allow this, the user will need to - # restart their work with a new instance of this class. + # restart with a new instance of this class. if (length(dupDatasets) > 0L) { dataCurrent <- dplyr::filter(dataCurrent, !name %in% dupDatasets) } @@ -625,11 +681,10 @@ DataCombined <- R6::R6Class( data <- dplyr::select(data, -dplyr::ends_with(c("Offsets", "ScaleFactors"))) # Datasets for which no data transformations were specified, there will be - # missing values, which need to be replaced by values representing no - # change. + # missing values, which need to be replaced by defaults for no change. data <- dplyr::left_join(data, private$.dataTransformations, by = "name") - # For offsets: 0 + # For offsets: `0` (default for no change) data <- dplyr::mutate( data, dplyr::across( @@ -638,7 +693,7 @@ DataCombined <- R6::R6Class( ) ) - # For scale factors: 1 + # For scale factors: `1` (default for no change) data <- dplyr::mutate( data, dplyr::across( @@ -651,7 +706,7 @@ DataCombined <- R6::R6Class( data <- dplyr::mutate(data, xValues = (xRawValues + xOffsets) * xScaleFactors, yValues = (yRawValues + yOffsets) * yScaleFactors, - yErrorValues = yRawErrorValues * yScaleFactors + yErrorValues = yRawErrorValues * abs(yScaleFactors) ) return(data) diff --git a/R/data-importer-configuration.R b/R/data-importer-configuration.R index 955bdbd51..bb8d91550 100644 --- a/R/data-importer-configuration.R +++ b/R/data-importer-configuration.R @@ -270,12 +270,12 @@ DataImporterConfiguration <- R6::R6Class( }, #' @description - #' Save configuration to a XML file that can be used in PKSim/MoBi + #' Save configuration to a XML file that can be used in PK-Sim/MoBi #' @param filePath Path (incl. file name) to the location where the configuration #' will be exported to. saveConfiguration = function(filePath) { validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) rClr::clrCall(private$.dataImporterTask, "SaveConfiguration", self$ref, filePath) invisible(self) diff --git a/R/data-set.R b/R/data-set.R index b0e47bda8..38c7a9d87 100644 --- a/R/data-set.R +++ b/R/data-set.R @@ -118,7 +118,7 @@ DataSet <- R6::R6Class( } if (!is.null((private$.yErrorColumn))) { - private$.yErrorColumn$displayUnit <- value + private$.setColumnUnit(private$.yErrorColumn, value) } invisible(self) }, @@ -168,6 +168,10 @@ DataSet <- R6::R6Class( return(toUnit(quantityOrDimension = private$.yColumn$dimension, values = lloq, targetUnit = private$.yColumn$displayUnit)) } + # Only one LLOQ value per data set is supported + if (!isOfLength(value, 1)) { + stop(messages$lloqOnlyScalar()) + } private$.yColumn$LLOQ <- toBaseUnit( quantityOrDimension = private$.yColumn$dimension, values = value, @@ -227,7 +231,7 @@ DataSet <- R6::R6Class( }, #' @description - #' Sets the xValues, yValues, and (optionally) yErrorValuues into the dataSet. + #' Sets the xValues, yValues, and (optionally) yErrorValues into the dataSet. #' Note: xValues, yValues and yErrorValues must have the same length #' @param xValues xValues to use #' @param yValues yValues to use diff --git a/R/default-plot-configuration.R b/R/default-plot-configuration.R index a7dbd9c62..78d0b96be 100644 --- a/R/default-plot-configuration.R +++ b/R/default-plot-configuration.R @@ -2,10 +2,13 @@ #' #' @description #' -#' R6 configuration class defining properties of plots that can be created with -#' `plotIndividualTimeProfile()`, `plotPopulationTimeProfile()`, +#' R6 configuration class defining aesthetic properties of plots that can be +#' created with `plotIndividualTimeProfile()`, `plotPopulationTimeProfile()`, #' `plotObservedVsSimulated()`, and `plotResidualsVsTime()`. #' +#' To interactively explore various aesthetic properties and appearance of plots +#' with these properties, you can use the [Shiny app](https://www.open-systems-pharmacology.org/TLF-Library/articles/theme-maker.html) from `{tlf}` package. +#' #' The following sections provide more details on how to customize it further. #' #' # Specifying aesthetic properties @@ -88,6 +91,13 @@ #' #' The available transformations can be seen in the `tlf::Scaling` list. #' +#' # Specifying tick labels +#' +#' `tlf::TickLabelTransforms` lists of all available tick label transformations. +#' For example, selecting `tlf::TickLabelTransforms$identity` will display tick +#' labels as they are, while selecting `tlf::TickLabelTransforms$log` will +#' display tick labels in logarithmic scale format. +#' #' # Saving plot #' #' By default, the plots will be shown in plot pane of your IDE, but the plots @@ -110,9 +120,9 @@ #' @field legendPosition A character string defining the legend position. #' Available options can be seen using `tlf::LegendPositions` list. #' @field legendTitleSize,legendTitleColor,legendTitleFontFamily,legendTitleFontFace,legendTitleAngle,legendTitleAlign Aesthetic properties for the legend title. -#' @field legendCaptionSize,legendCaptionColor,legendCaptionFontFamily,legendCaptionFontFace,legendCaptionAngle,legendCaptionAlign Aesthetic properties for the legend caption. -#' @field xAxisTicksLabels,xAxisLabelTicksSize,xAxisLabelTicksColor,xAxisLabelTicksFontFamily,xAxisLabelTicksFontFace,xAxisLabelTicksAngle,xAxisLabelTicksAlign Aesthetic properties for the x-axis label. -#' @field yAxisTicksLabels,yAxisLabelTicksSize,yAxisLabelTicksColor,yAxisLabelTicksFontFamily,yAxisLabelTicksFontFace,yAxisLabelTicksAngle,yAxisLabelTicksAlign Aesthetic properties for the y-axis label. +#' @field legendKeysSize,legendKeysColor,legendKeysFontFamily,legendKeysFontFace,legendKeysAngle,legendKeysAlign Aesthetic properties for the legend caption. +#' @field xAxisTicksLabels,xAxisLabelTicksSize,xAxisLabelTicksColor,xAxisLabelTicksFontFamily,xAxisLabelTicksFontFace,xAxisLabelTicksAngle,xAxisLabelTicksAlign,xAxisExpand Aesthetic properties for the x-axis label. +#' @field yAxisTicksLabels,yAxisLabelTicksSize,yAxisLabelTicksColor,yAxisLabelTicksFontFamily,yAxisLabelTicksFontFace,yAxisLabelTicksAngle,yAxisLabelTicksAlign,yAxisExpand Aesthetic properties for the y-axis label. #' @field xAxisLimits,yAxisLimits A numeric vector of axis limits for the x-and #' y-axis, respectively. #' @field xAxisTicks,yAxisTicks A numeric vector or a function defining where to @@ -129,7 +139,7 @@ #' @field linesColor,linesSize,linesLinetype,linesAlpha A selection key or values for choice of color, fill, shape, size, linetype, alpha, respectively, for lines. #' @field pointsColor,pointsShape,pointsSize,pointsAlpha A selection key or values for choice of color, fill, shape, size, linetype, alpha, respectively, for points. #' @field ribbonsFill,ribbonsSize,ribbonsLinetype,ribbonsAlpha A selection key or values for choice of color, fill, shape, size, linetype, alpha, respectively, for ribbons. -#' @field errorbarsSize,errorbarsLinetype,errorbarsAlpha A selection key or values for choice of color, fill, shape, size, linetype, alpha, respectively, for errorbars. +#' @field errorbarsSize,errorbarsLinetype,errorbarsAlpha,errorbarsCapSize A selection key or values for choice of color, fill, shape, size, linetype, alpha, cap width/height, respectively, for error bars. #' #' @examples #' @@ -161,7 +171,7 @@ DefaultPlotConfiguration <- R6::R6Class( title = NULL, titleColor = "black", - titleSize = 12, + titleSize = tlf::PlotAnnotationTextSize$plotTitleSize, titleFontFace = tlf::FontFaces$plain, titleFontFamily = "", titleAngle = 0, @@ -171,7 +181,7 @@ DefaultPlotConfiguration <- R6::R6Class( subtitle = NULL, subtitleColor = "black", - subtitleSize = 10, + subtitleSize = tlf::PlotAnnotationTextSize$plotSubtitleSize, subtitleFontFace = tlf::FontFaces$plain, subtitleFontFamily = "", subtitleAngle = 0, @@ -181,7 +191,7 @@ DefaultPlotConfiguration <- R6::R6Class( caption = NULL, captionColor = "black", - captionSize = 8, + captionSize = tlf::PlotAnnotationTextSize$plotCaptionSize, captionFontFace = tlf::FontFaces$plain, captionFontFamily = "", captionAngle = 0, @@ -191,7 +201,7 @@ DefaultPlotConfiguration <- R6::R6Class( xLabel = NULL, xLabelColor = "black", - xLabelSize = 10, + xLabelSize = tlf::PlotAnnotationTextSize$plotXLabelSize, xLabelFontFace = tlf::FontFaces$plain, xLabelFontFamily = "", xLabelAngle = 0, @@ -201,7 +211,7 @@ DefaultPlotConfiguration <- R6::R6Class( yLabel = NULL, yLabelColor = "black", - yLabelSize = 10, + yLabelSize = tlf::PlotAnnotationTextSize$plotYLabelSize, yLabelFontFace = tlf::FontFaces$plain, yLabelFontFamily = "", yLabelAngle = 90, @@ -211,28 +221,28 @@ DefaultPlotConfiguration <- R6::R6Class( legendPosition = NULL, legendTitle = NULL, - legendTitleSize = 10, + legendTitleSize = tlf::PlotAnnotationTextSize$plotLegendTitleSize, legendTitleColor = "black", legendTitleFontFamily = "", legendTitleFontFace = tlf::FontFaces$plain, legendTitleAngle = 0, - legendTitleAlign = tlf::Alignments$center, + legendTitleAlign = tlf::Alignments$left, - # legendCaption ------------------------------------ + # legendKeys ------------------------------------ - legendCaptionSize = 10, - legendCaptionColor = "black", - legendCaptionFontFamily = "", - legendCaptionFontFace = tlf::FontFaces$plain, - legendCaptionAngle = 0, - legendCaptionAlign = tlf::Alignments$center, + legendKeysSize = tlf::PlotAnnotationTextSize$plotLegendCaptionSize, + legendKeysColor = "black", + legendKeysFontFamily = "", + legendKeysFontFace = tlf::FontFaces$plain, + legendKeysAngle = 0, + legendKeysAlign = tlf::Alignments$left, # XAxisConfiguration ------------------------------------ xAxisLimits = NULL, - xAxisScale = tlf::Scaling$lin, + xAxisScale = NULL, xAxisTicks = NULL, - xAxisTicksLabels = NULL, + xAxisTicksLabels = tlf::TickLabelTransforms$identity, xAxisLabelTicksSize = NULL, xAxisLabelTicksColor = "black", xAxisLabelTicksFontFamily = "", @@ -243,9 +253,9 @@ DefaultPlotConfiguration <- R6::R6Class( # YAxisConfiguration ------------------------------------ yAxisLimits = NULL, - yAxisScale = tlf::Scaling$lin, + yAxisScale = NULL, yAxisTicks = NULL, - yAxisTicksLabels = NULL, + yAxisTicksLabels = tlf::TickLabelTransforms$identity, yAxisLabelTicksSize = NULL, yAxisLabelTicksColor = "black", yAxisLabelTicksFontFamily = "", @@ -256,7 +266,7 @@ DefaultPlotConfiguration <- R6::R6Class( # watermark ------------------------------------ watermark = NULL, - watermarkSize = 20, + watermarkSize = tlf::PlotAnnotationTextSize$plotWatermarkSize, watermarkColor = "grey40", watermarkFontFamily = "", watermarkFontFace = tlf::FontFaces$plain, @@ -282,12 +292,14 @@ DefaultPlotConfiguration <- R6::R6Class( xAxisColor = "black", xAxisSize = 0.5, xAxisLinetype = tlf::Linetypes$solid, + xAxisExpand = FALSE, # yAxis ------------------------------------ yAxisColor = "black", yAxisSize = 0.5, yAxisLinetype = tlf::Linetypes$solid, + yAxisExpand = FALSE, # xGrid ------------------------------------ @@ -314,8 +326,8 @@ DefaultPlotConfiguration <- R6::R6Class( # There is no `pointsFill` because it doesn't make sense to "fill" a line # with color. There is already `pointsColor` for that. - pointsColor = NULL, - pointsShape = NULL, + pointsColor = tlf::ColorMaps$ospDefault, + pointsShape = names(tlf::Shapes), pointsSize = 3, pointsAlpha = 0.75, @@ -333,6 +345,7 @@ DefaultPlotConfiguration <- R6::R6Class( # Color and fill are taken from point mapping, therefore no # `errorbarsColor`, `errorbarsFill` parameters errorbarsSize = 1, + errorbarsCapSize = 5, errorbarsLinetype = tlf::Linetypes$solid, errorbarsAlpha = 0.75 ) diff --git a/R/dot-net-wrapper.R b/R/dot-net-wrapper.R index 19149a2b0..cecaba07f 100644 --- a/R/dot-net-wrapper.R +++ b/R/dot-net-wrapper.R @@ -134,7 +134,7 @@ DotNetWrapper <- R6::R6Class( } }, throwPropertyIsReadonly = function(propertyName) { - stop(messages$errorPropertyReadOnly(propertyName), call. = FALSE) + stop(messages$errorPropertyReadOnly(propertyName)) }, # maybe dispose should be called to if available diff --git a/R/get-net-task.R b/R/get-net-task.R index a7b78221e..accd6fee5 100644 --- a/R/get-net-task.R +++ b/R/get-net-task.R @@ -1,5 +1,4 @@ -#' @title Returns an instance of the specified `.NET` Task -#' @name .getNetTask +#' Get an instance of the specified `.NET` Task #' #' @param taskName The name of the task to retrieve (**without** `Get` prefix). #' diff --git a/R/init-package.R b/R/init-package.R index 464b85fac..db52c9d22 100644 --- a/R/init-package.R +++ b/R/init-package.R @@ -4,7 +4,7 @@ #' #' @import rClr #' @keywords internal -initPackage <- function() { +.initPackage <- function() { filePathFor <- function(name) { system.file("lib", name, package = ospsuiteEnv$packageName) } @@ -20,5 +20,5 @@ initPackage <- function() { rClr::clrCallStatic("OSPSuite.R.Api", "InitializeOnce", apiConfig$ref) - initializeDimensionAndUnitLists() + .initializeDimensionAndUnitLists() } diff --git a/R/interval.R b/R/interval.R index 7c9a773c8..0ff3dfc0e 100644 --- a/R/interval.R +++ b/R/interval.R @@ -1,8 +1,8 @@ #' @title Interval #' @docType class #' @description Simulation Interval (typically associated with an instance of `OutputSchema`) -#' #' @format NULL +#' @keywords internal Interval <- R6::R6Class( "Interval", cloneable = FALSE, diff --git a/R/messages.R b/R/messages.R index b71a6f5e2..d5b6307a4 100644 --- a/R/messages.R +++ b/R/messages.R @@ -26,10 +26,43 @@ messages$unpairableDatasetsRemoved <- function() { "Following non-grouped or unpairable datasets have been removed" } +messages$valuesNotInterpolated <- function() { + "Predicted values couldn't be interpolated at following time points" +} + messages$printMultipleEntries <- function(header, entries) { message(paste0(header, ":\n"), paste0(entries, collapse = "\n")) } +messages$linearScaleWithFoldDistance <- function() { + "Linear scale is inappropriate when `foldDistance` argument is specified." +} + messages$errorLoadingUnitsForDimension <- function(dimensions) { messages$printMultipleEntries("Could not load units for the following dimensions", dimensions) } + +messages$plottingWithEmptyDataCombined <- function() { + "No plot can be created because the entered `DataCombined` object does not contain any datasets." +} + +messages$residualsCanNotBeComputed <- function() { + "No residuals can be computed because the entered `DataCombined` object does not contain any observed-simulated datasets that can be paired." +} + +messages$logScaleNotAllowed <- function() { + "The Y-axis for this plot should not be on a log scale, since the residuals are expected to be centered around 0." +} + +messages$lloqOnlyScalar <- function() { + "Only one LLOQ value per `DataSet` is supported! Please provide a scalar value and not a vector." +} + +messages$simBatchStartValueNaN <- function(entityPaths) { + paste0("Start values of the entities with paths '", paste(entityPaths, collapse = ", "), "' is `NaN`! Cannot add such run values set") +} + +messages$plotObservedVsSimulatedWrongFoldDistance <- function(parameterName, foldDistances) { + paste0("Parameter '", parameterName, "' should be >1! Following values have + been passed: '", paste(foldDistances, collapse = ", "), "'.") +} diff --git a/R/molecule.R b/R/molecule.R index 620b9a01d..b54c7f622 100644 --- a/R/molecule.R +++ b/R/molecule.R @@ -7,7 +7,7 @@ #' @docType class #' @name Molecule #' -#' @keywords Molecule +#' @keywords internal #' @format NULL Molecule <- R6::R6Class( "Molecule", @@ -27,7 +27,7 @@ Molecule <- R6::R6Class( } }, #' @field scaleDivisor Scale divisor. Its purpose is to reduce numerical noise and to enhance computation performance. - #' see https://docs.open-systems-pharmacology.org/working-with-mobi/mobi-documentation/model-building-components#import-molecule-and-parameter-start-values-from-excel + #' see \url{https://docs.open-systems-pharmacology.org/working-with-mobi/mobi-documentation/model-building-components#import-molecule-and-parameter-start-values-from-excel} scaleDivisor = function(value) { private$wrapProperty("ScaleDivisor", value) } diff --git a/R/ospsuite-env.R b/R/ospsuite-env.R index e124afb4f..a9b111068 100644 --- a/R/ospsuite-env.R +++ b/R/ospsuite-env.R @@ -54,6 +54,9 @@ ospsuiteEnv$isPKSimLoaded <- FALSE # NetTask `DimensionTask` cached for performance benefits. Created the first time it is requested. ospsuiteEnv$dimensionTask <- NULL +# Small value added to zero when calculating log +ospsuiteEnv$LOG_SAFE_EPSILON <- 1e-20 + #' Names of the settings stored in ospsuiteEnv. Can be used with `getOSPSuiteSetting()` #' @include utilities.R #' @export diff --git a/R/output-selections.R b/R/output-selections.R index badc01531..00be150c7 100644 --- a/R/output-selections.R +++ b/R/output-selections.R @@ -12,7 +12,7 @@ OutputSelections <- R6::R6Class( if (missing(value)) { .toObjectType(rClr::clrGet(self$ref, "OutputsAsArray"), QuantitySelection) } else { - stop(messages$errorPropertyReadOnly("allOutputs"), call. = FALSE) + stop(messages$errorPropertyReadOnly("allOutputs")) } } ), diff --git a/R/parameter-range.R b/R/parameter-range.R index 5b57375e7..7f14abfcd 100644 --- a/R/parameter-range.R +++ b/R/parameter-range.R @@ -68,7 +68,7 @@ ParameterRange <- R6::R6Class( ) ) -createParameterRange <- function(min, max, unit) { +.createParameterRange <- function(min, max, unit) { if (is.null(min) && is.null(max)) { return(NULL) } diff --git a/R/parameter.R b/R/parameter.R index e8f0c5198..c71339f27 100644 --- a/R/parameter.R +++ b/R/parameter.R @@ -18,7 +18,8 @@ Parameter <- R6::R6Class( ), active = list( #' @field isStateVariable Returns `TRUE` is the parameter has a RHS otherwise `FALSE`. - #' @details Setting the value to `FALSE` will delete the RHS Formula. Setting it to `TRUE` is not currently supported and will throw an error + #' Setting the value to `FALSE` will delete the RHS Formula. + #' Setting it to `TRUE` is not currently supported and will throw an error. isStateVariable = function(value) { hasRHSFormula <- !is.null(private$.rhsFormula) if (missing(value)) { @@ -33,7 +34,7 @@ Parameter <- R6::R6Class( # Set to true and no RHS => error if (value) { - stop(messages$errorCannotSetRHSFormula, call. = FALSE) + stop(messages$errorCannotSetRHSFormula) } # we are deleting the RHS Formula diff --git a/R/path-explorer.R b/R/path-explorer.R deleted file mode 100644 index dedfaab8f..000000000 --- a/R/path-explorer.R +++ /dev/null @@ -1,91 +0,0 @@ - -addBranch <- function(originalPathString, arrayToGo) { - # Function to create a multilayered list called endList with a branched structure corresponding to the structure of arrayToGo that terminates with a string called 'path' that is equal to the string originalString - if (length(arrayToGo) == 0) { - # If arrayToGo is empty, create a terminal list with a string called 'path' and value equal to originalString - endList <- list() - endList$path <- originalPathString - return(endList) - } else { - # If arrayToGo is still not empty, remove its leading element and create a sub-branch list corresponding to the structure of the remaining elements of arrayToGo - newBranch <- list() - newBranch[[arrayToGo[1]]] <- addBranch(originalPathString, tail(arrayToGo, -1)) - return(newBranch) - } -} - -nextStep <- function(listSoFar, originalString, arrayToGo) { - # Recursive function that adds a multilayer list to listSoFar that has a branched structure representing the vector of strings arrayToGo. - if (length(arrayToGo) == 0) { # If end of string vector arrayToGo has been reached, create a vector called 'path' and give it the value 'originalString'. - listSoFar$path <- originalString - } else { # End of branch has not been reached. - # If this portion of the string vector arrayToGo has not been added to listToGo yet, add it using the function addBranch - if (is.null(listSoFar[[arrayToGo[1]]])) { - listSoFar[[arrayToGo[1]]] <- addBranch(originalString, tail(arrayToGo, -1)) - } - # If this portion of the string vector arrayToGo has already been added to listSoFar, remove the leading element of arrayToGo and recursively apply this function using the remaining elements of arrayToGo. - else { - listSoFar[[arrayToGo[1]]] <- nextStep(listSoFar[[arrayToGo[1]]], originalString, tail(arrayToGo, -1)) - } - } - return(listSoFar) -} - - - -#' Given a simulation file path or an instance of a simulation, traverses the simulation structure and returns a tree like structure -#' allowing for intuitive navigation in the simulation tree -# -#' @param simulationOrFilePath Full path of the simulation to load or instance of a simulation -#' @param quantityType A vector of strings that specify the types of the entities to be included in the tree. The types can be any combination of "Quantity", "Molecule", "Parameter" and "Observer" -#' @return A list with a branched structure representing the path tree of entities in the simulation file that fall under the types specified in `quantityType`. -#' At the end of each branch is a string called 'path' that is the path of the quantity represented by the branch. -#' -#' @importFrom utils tail -#' @examples -#' simPath <- system.file("extdata", "simple.pkml", package = "ospsuite") -#' sim <- loadSimulation(simPath) -#' -#' tree <- getSimulationTree(sim) -#' -#' liver_volume_path <- tree$Organism$Liver$Volume$path -#' @export -getSimulationTree <- function(simulationOrFilePath, quantityType = "Quantity") { - validateIsOfType(simulationOrFilePath, c("Simulation", "character")) - - quantityTypeList <- list( - "Quantity" = getAllQuantityPathsIn, - "Molecule" = getAllMoleculePathsIn, - "Parameter" = getAllParameterPathsIn, - "Observer" = getAllObserverPathsIn - ) - - validateIsIncluded(values = quantityType, parentValues = names(quantityTypeList)) - - simulation <- simulationOrFilePath - if (isOfType(simulationOrFilePath, "character")) { - simulation <- loadSimulation(simulationOrFilePath) - } - - # Build a vector, with no duplicated entries, of all paths corresponding to - # entities in `simulation` that fall under the types specified in quantityType - allPaths <- sapply(quantityType, function(type) { - quantityTypeList[[type]](simulation) - }) %>% - unname() %>% - unlist() %>% - unique() - - # Initiate list to be returned as a null list. - pathEnumList <- list() - - for (path in allPaths) { - # Convert the path string to a vector of strings, each representing a branch portion. - pathArray <- toPathArray(path) - - # Begin recursive loop to generate branched list. - pathEnumList <- nextStep(pathEnumList, path, pathArray) - } - - return(pathEnumList) -} diff --git a/R/plot-individual-time-profile.R b/R/plot-individual-time-profile.R index 03b25cf39..f7c6ad971 100644 --- a/R/plot-individual-time-profile.R +++ b/R/plot-individual-time-profile.R @@ -1,6 +1,6 @@ #' Time-profile plot of individual data #' -#' @param dataCombined A `DataCombined` object. +#' @inheritParams calculateResiduals #' @param defaultPlotConfiguration A `DefaultPlotConfiguration` object, which is #' an `R6` class object that defines plot properties. #' @@ -9,8 +9,41 @@ #' @family plotting #' #' @examples +#' # simulated data +#' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +#' sim <- loadSimulation(simFilePath) +#' simResults <- runSimulation(sim) +#' outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)" #' -#' # TODO: add example +#' # observed data +#' obsData <- lapply( +#' c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"), +#' function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite")) +#' ) +#' names(obsData) <- lapply(obsData, function(x) x$name) +#' +#' +#' # Create a new instance of `DataCombined` class +#' myDataCombined <- DataCombined$new() +#' +#' # Add simulated results +#' myDataCombined$addSimulationResults( +#' simulationResults = simResults, +#' quantitiesOrPaths = outputPath, +#' groups = "Aciclovir PVB" +#' ) +#' +#' # Add observed data set +#' myDataCombined$addDataSets(obsData$`Vergin 1995.Iv`, groups = "Aciclovir PVB") +#' +#' # Create a new instance of `DefaultPlotConfiguration` class +#' myPlotConfiguration <- DefaultPlotConfiguration$new() +#' myPlotConfiguration$title <- "My Plot Title" +#' myPlotConfiguration$subtitle <- "My Plot Subtitle" +#' myPlotConfiguration$caption <- "My Sources" +#' +#' # plot +#' plotIndividualTimeProfile(myDataCombined, myPlotConfiguration) #' #' @export plotIndividualTimeProfile <- function(dataCombined, @@ -28,40 +61,33 @@ plotIndividualTimeProfile <- function(dataCombined, quantiles = NULL) { # validation ----------------------------- - validateIsOfType(defaultPlotConfiguration, "DefaultPlotConfiguration", nullAllowed = TRUE) - defaultPlotConfiguration <- defaultPlotConfiguration %||% DefaultPlotConfiguration$new() - validateIsOfType(dataCombined, "DataCombined") - validateIsSameLength(objectCount(dataCombined), 1L) # only single instance is allowed - - # data frames ----------------------------- - - combinedData <- dataCombined$toDataFrame() + defaultPlotConfiguration <- .validateDefaultPlotConfiguration(defaultPlotConfiguration) - # Getting all units on the same scale - combinedData <- .unitConverter(combinedData, defaultPlotConfiguration$xUnit, defaultPlotConfiguration$yUnit) - - # Datasets which haven't been assigned to any group will be plotted as a group - # on its own. That is, the `group` column entries for them will be their names. - combinedData <- .addMissingGroupings(combinedData) + .validateDataCombinedForPlotting(dataCombined) + if (is.null(dataCombined$groupMap)) { + return(NULL) + } # `TimeProfilePlotConfiguration` object ----------------------------- - # Create an instance of `TimeProfilePlotConfiguration` class by doing a - # one-to-one mapping of internal plot configuration object's public fields + # Create an instance of plot-specific class object timeProfilePlotConfiguration <- .convertGeneralToSpecificPlotConfiguration( - data = combinedData, specificPlotConfiguration = tlf::TimeProfilePlotConfiguration$new(), generalPlotConfiguration = defaultPlotConfiguration ) + # data frames ----------------------------- + + # Getting all units on the same scale + combinedData <- convertUnits(dataCombined, defaultPlotConfiguration$xUnit, defaultPlotConfiguration$yUnit) + + # Datasets which haven't been assigned to any group will be plotted as a group + # on its own. That is, the `group` column entries for them will be their names. + combinedData <- .addMissingGroupings(combinedData) + # axes labels ----------------------------- - # The type of plot can be guessed from the specific `PlotConfiguration` object - # used, since each plot has a unique corresponding class. The labels can then - # be prepared accordingly. - axesLabels <- .createAxesLabels(combinedData, timeProfilePlotConfiguration) - timeProfilePlotConfiguration$labels$xlabel$text <- timeProfilePlotConfiguration$labels$xlabel$text %||% axesLabels$xLabel - timeProfilePlotConfiguration$labels$ylabel$text <- timeProfilePlotConfiguration$labels$ylabel$text %||% axesLabels$yLabel + timeProfilePlotConfiguration <- .updatePlotConfigurationAxesLabels(combinedData, timeProfilePlotConfiguration) # plot ----------------------------- @@ -69,6 +95,8 @@ plotIndividualTimeProfile <- function(dataCombined, if (nrow(obsData) == 0) { obsData <- NULL + } else { + obsData <- .computeBoundsFromErrorType(obsData) } simData <- as.data.frame(dplyr::filter(combinedData, dataType == "simulated")) @@ -82,34 +110,57 @@ plotIndividualTimeProfile <- function(dataCombined, simData <- as.data.frame(.extractAggregatedSimulatedData(simData, quantiles)) } + # To avoid repetition, assign column names to variables and use them instead + x <- "xValues" + y <- "yValues" + ymin <- "yValuesLower" + ymax <- "yValuesHigher" + color <- fill <- "group" + linetype <- shape <- "name" + + # population time profile mappings ------------------------------ + + # The exact mappings chosen will depend on whether there are multiple datasets + # of a given type present per group if (!is.null(quantiles)) { - dataMapping <- tlf::TimeProfileDataMapping$new( - x = "xValues", - y = "yValuesCentral", - ymin = "yValuesLower", - ymax = "yValuesHigher", - group = "group" + simulatedDataMapping <- tlf::TimeProfileDataMapping$new(x, y, ymin, ymax, + color = color, + linetype = linetype, + fill = fill ) - } else { - dataMapping <- tlf::TimeProfileDataMapping$new( - x = "xValues", - y = "yValues", - group = "group" + + observedDataMapping <- tlf::ObservedDataMapping$new(x, y, ymin, ymax, + shape = shape, + color = color ) } + # individual time profile mappings ------------------------------ + + if (is.null(quantiles)) { + simulatedDataMapping <- tlf::TimeProfileDataMapping$new(x, y, + color = color, + linetype = linetype + ) + + observedDataMapping <- tlf::ObservedDataMapping$new(x, y, ymin, ymax, + shape = shape, + color = color + ) + } + + tlf::setDefaultErrorbarCapSize(defaultPlotConfiguration$errorbarsCapSize) + profilePlot <- tlf::plotTimeProfile( data = simData, - dataMapping = dataMapping, + dataMapping = simulatedDataMapping, observedData = obsData, - observedDataMapping = tlf::ObservedDataMapping$new( - x = "xValues", - y = "yValues", - group = "group", - error = "yErrorValues" - ), + observedDataMapping = observedDataMapping, plotConfiguration = timeProfilePlotConfiguration ) + # Suppress certain mappings in the legend + profilePlot <- profilePlot + ggplot2::guides(linetype = "none", shape = "none") + return(profilePlot) } diff --git a/R/plot-observed-vs-simulated.R b/R/plot-observed-vs-simulated.R new file mode 100644 index 000000000..99a432a7b --- /dev/null +++ b/R/plot-observed-vs-simulated.R @@ -0,0 +1,194 @@ +#' Observed versus predicted/simulated scatter plot +#' +#' @inheritParams plotIndividualTimeProfile +#' @param foldDistance A vector for plotting lines at required fold distances +#' The vector can include only fold distance values `>1`. An +#' `x`-fold distance is defined as all simulated values within the range +#' between `x`-fold (depicted by the upper fold range line) and `1/x`-fold +#' (depicted by the lower fold range line) of observed values. The identity +#' line can be interpreted as the `1`-fold range. +#' +#' @import tlf +#' +#' @family plotting +#' +#' @examples +#' # simulated data +#' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +#' sim <- loadSimulation(simFilePath) +#' simResults <- runSimulation(sim) +#' outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)" +#' +#' # observed data +#' obsData <- lapply( +#' c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"), +#' function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite")) +#' ) +#' names(obsData) <- lapply(obsData, function(x) x$name) +#' +#' +#' # Create a new instance of `DataCombined` class +#' myDataCombined <- DataCombined$new() +#' +#' # Add simulated results +#' myDataCombined$addSimulationResults( +#' simulationResults = simResults, +#' quantitiesOrPaths = outputPath, +#' groups = "Aciclovir PVB" +#' ) +#' +#' # Add observed data set +#' myDataCombined$addDataSets(obsData$`Vergin 1995.Iv`, groups = "Aciclovir PVB") +#' +#' # Create a new instance of `DefaultPlotConfiguration` class +#' myPlotConfiguration <- DefaultPlotConfiguration$new() +#' myPlotConfiguration$title <- "My Plot Title" +#' myPlotConfiguration$subtitle <- "My Plot Subtitle" +#' myPlotConfiguration$caption <- "My Sources" +#' +#' # plot +#' plotObservedVsSimulated(myDataCombined, myPlotConfiguration) +#' @export +plotObservedVsSimulated <- function(dataCombined, + defaultPlotConfiguration = NULL, + foldDistance = 2) { + # validation ----------------------------- + + defaultPlotConfiguration <- .validateDefaultPlotConfiguration(defaultPlotConfiguration) + + .validateDataCombinedForPlotting(dataCombined) + if (is.null(dataCombined$groupMap)) { + return(NULL) + } + + # `ObsVsPredPlotConfiguration` object ----------------------------- + + # Create an instance of plot-specific class object + obsVsPredPlotConfiguration <- .convertGeneralToSpecificPlotConfiguration( + specificPlotConfiguration = tlf::ObsVsPredPlotConfiguration$new(), + generalPlotConfiguration = defaultPlotConfiguration + ) + + # Linear scaling is stored as identity scaling in `{tlf}` + is_any_scale_linear <- ( + obsVsPredPlotConfiguration$xAxis$scale == tlf::Scaling$identity || + obsVsPredPlotConfiguration$yAxis$scale == tlf::Scaling$identity + ) + + # The argument `foldDistance` should only include fold values different from + # the default value, which must always be present. + # + # The default value depends on the scale: + # + # - For linear scale: `1` + # - For logarithmic scale: `0` + defaultFoldDistance <- ifelse(is_any_scale_linear, 0, 1) + + # foldDistance should be above 1 + if (any(foldDistance <= 1)) { + stop(messages$plotObservedVsSimulatedWrongFoldDistance("foldDistance", foldDistance)) + } + + if (!any(dplyr::near(defaultFoldDistance, foldDistance))) { + foldDistance <- c(defaultFoldDistance, foldDistance) + } + + if (is_any_scale_linear && !is.null(foldDistance)) { + warning(messages$linearScaleWithFoldDistance()) + foldDistance <- 0 + } + + # data frames ----------------------------- + + # Create a paired data frame (observed versus simulated) from `DataCombined` object. + # + # `DefaultPlotConfiguration` provides units for conversion. + # `PlotConfiguration` provides scaling details needed while computing residuals. + pairedData <- calculateResiduals(dataCombined, + scaling = obsVsPredPlotConfiguration$yAxis$scale, + xUnit = defaultPlotConfiguration$xUnit, + yUnit = defaultPlotConfiguration$yUnit + ) + + # Quit early if there is no data to visualize. + if (is.null(pairedData)) { + return(NULL) + } + + # In logarithmic scale, if any of the values are `0`, plotting will fail. + # + # To avoid this, just remove rows where any of the quantities are `0`s. + if (obsVsPredPlotConfiguration$yAxis$scale %in% c(tlf::Scaling$log, tlf::Scaling$ln)) { + pairedData <- dplyr::filter( + pairedData, + yValuesObserved != 0, yValuesSimulated != 0 + ) + } + + # Add minimum and maximum values for observed data to plot error bars + pairedData <- dplyr::mutate( + pairedData, + yValuesObservedLower = yValuesObserved - yErrorValues, + yValuesObservedHigher = yValuesObserved + yErrorValues, + .after = yValuesObserved # Create new columns after `yValuesObserved` column + ) + + # Time points at which predicted values can't be interpolated, and need to be + # extrapolated. + # + # This will happen in rare case scenarios where simulated data is sampled at a + # lower frequency than observed data. + predictedValuesMissingIndices <- which(is.na(pairedData$yValuesSimulated)) + + # Warn the user about failure to interpolate. + if (length(predictedValuesMissingIndices) > 0) { + warning( + messages$printMultipleEntries( + header = messages$valuesNotInterpolated(), + entries = pairedData$xValues[predictedValuesMissingIndices] + ) + ) + } + + # axes labels ----------------------------- + + obsVsPredPlotConfiguration <- .updatePlotConfigurationAxesLabels(pairedData, obsVsPredPlotConfiguration) + + # plot ----------------------------- + + tlf::setDefaultErrorbarCapSize(defaultPlotConfiguration$errorbarsCapSize) + + # Since groups might include more than one observed dataset (indicated by shape) + # in a group (indicated by color), we have to override the default shape legend + # and assign a manual shape to each legend entry + # The shapes follow the settings in the user-provided plot configuration + overrideShapeAssignment <- pairedData %>% + dplyr::select(name, group) %>% + dplyr::distinct() %>% + dplyr::arrange(name) %>% + dplyr::mutate(shapeAssn = obsVsPredPlotConfiguration$points$shape[1:nrow(.)]) %>% + dplyr::filter(!duplicated(group)) + + plotObject <- tlf::plotObsVsPred( + data = as.data.frame(pairedData), + dataMapping = tlf::ObsVsPredDataMapping$new( + x = "yValuesObserved", + y = "yValuesSimulated", + group = "group", + xmin = "yValuesObservedLower", + xmax = "yValuesObservedHigher", + shape = "name" + ), + foldDistance = foldDistance, + plotConfiguration = obsVsPredPlotConfiguration + ) + + return(plotObject + ggplot2::guides( + shape = "none", + col = ggplot2::guide_legend( + title = obsVsPredPlotConfiguration$legend$title$text, + title.theme = obsVsPredPlotConfiguration$legend$title$createPlotFont(), + override.aes = list(shape = overrideShapeAssignment$shapeAssn) + ) + )) +} diff --git a/R/plot-population-time-profile.R b/R/plot-population-time-profile.R index cadeaf223..95d2d0efe 100644 --- a/R/plot-population-time-profile.R +++ b/R/plot-population-time-profile.R @@ -11,15 +11,31 @@ #' @family plotting #' #' @examples +#' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +#' sim <- loadSimulation(simFilePath) #' -#' # TODO: add example +#' populationResults <- importResultsFromCSV( +#' simulation = sim, +#' filePaths = system.file("extdata", "SimResults_pop.csv", package = "ospsuite") +#' ) +#' +#' # Create a new instance of `DataCombined` class +#' myDataComb <- DataCombined$new() +#' myDataComb$addSimulationResults(populationResults) +#' +#' # Create a new instance of `DefaultPlotConfiguration` class +#' myPlotConfiguration <- DefaultPlotConfiguration$new() +#' myPlotConfiguration$title <- "My Plot Title" +#' myPlotConfiguration$subtitle <- "My Plot Subtitle" +#' myPlotConfiguration$caption <- "My Sources" +#' +#' # plot +#' plotPopulationTimeProfile(myDataComb, myPlotConfiguration) #' #' @export plotPopulationTimeProfile <- function(dataCombined, defaultPlotConfiguration = NULL, quantiles = c(0.05, 0.5, 0.95)) { - # validation ----------------------------- - validateIsNumeric(quantiles, nullAllowed = FALSE) validateIsOfLength(quantiles, 3L) diff --git a/R/plot-residuals-vs-simulated.R b/R/plot-residuals-vs-simulated.R new file mode 100644 index 000000000..4dfeb9338 --- /dev/null +++ b/R/plot-residuals-vs-simulated.R @@ -0,0 +1,127 @@ +#' Residuals versus time scatter plot +#' +#' @inheritParams plotIndividualTimeProfile +#' @inheritParams tlf::plotResVsPred +#' +#' @import tlf +#' +#' @family plotting +#' +#' @examples +#' +#' # simulated data +#' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +#' sim <- loadSimulation(simFilePath) +#' simResults <- runSimulation(sim) +#' outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)" +#' +#' # observed data +#' obsData <- lapply( +#' c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"), +#' function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite")) +#' ) +#' names(obsData) <- lapply(obsData, function(x) x$name) +#' +#' +#' # Create a new instance of `DataCombined` class +#' myDataCombined <- DataCombined$new() +#' +#' # Add simulated results +#' myDataCombined$addSimulationResults( +#' simulationResults = simResults, +#' quantitiesOrPaths = outputPath, +#' groups = "Aciclovir PVB" +#' ) +#' +#' # Add observed data set +#' myDataCombined$addDataSets(obsData$`Vergin 1995.Iv`, groups = "Aciclovir PVB") +#' +#' # Create a new instance of `DefaultPlotConfiguration` class +#' myPlotConfiguration <- DefaultPlotConfiguration$new() +#' myPlotConfiguration$title <- "My Plot Title" +#' myPlotConfiguration$subtitle <- "My Plot Subtitle" +#' myPlotConfiguration$caption <- "My Sources" +#' +#' # plot +#' plotResidualsVsSimulated(myDataCombined, myPlotConfiguration) +#' +#' @export +plotResidualsVsSimulated <- function(dataCombined, + defaultPlotConfiguration = NULL) { + # validation ----------------------------- + + defaultPlotConfiguration <- .validateDefaultPlotConfiguration(defaultPlotConfiguration) + + .validateDataCombinedForPlotting(dataCombined) + if (is.null(dataCombined$groupMap)) { + return(NULL) + } + + # `ResVsPredPlotConfiguration` object ----------------------------- + + # Create an instance of plot-specific class object + resVsPredPlotConfiguration <- .convertGeneralToSpecificPlotConfiguration( + specificPlotConfiguration = tlf::ResVsPredPlotConfiguration$new(), + generalPlotConfiguration = defaultPlotConfiguration + ) + + # This should never be the case as the residuals should be centered around 0. + is_y_scale_logarithmic <- resVsPredPlotConfiguration$yAxis$scale == "log" + if (is_y_scale_logarithmic) { + stop(messages$logScaleNotAllowed()) + } + + # data frames ----------------------------- + + # Create a paired data frame (observed versus simulated) from `DataCombined` object. + # + # `DefaultPlotConfiguration` provides units for conversion. + # `PlotConfiguration` provides scaling details needed while computing residuals. + pairedData <- calculateResiduals(dataCombined, + scaling = resVsPredPlotConfiguration$yAxis$scale, + xUnit = defaultPlotConfiguration$xUnit, + yUnit = defaultPlotConfiguration$yUnit + ) + + # Quit early if there is no data to visualize. + if (is.null(pairedData)) { + return(NULL) + } + + # Since groups might include more than one observed dataset (indicated by shape) + # in a group (indicated by color), we have to override the default shape legend + # and assign a manual shape to each legend entry + # The shapes follow the settings in the user-provided plot configuration + overrideShapeAssignment <- pairedData %>% + dplyr::select(name, group) %>% + dplyr::distinct() %>% + dplyr::arrange(name) %>% + dplyr::mutate(shapeAssn = resVsPredPlotConfiguration$points$shape[1:nrow(.)]) %>% + dplyr::filter(!duplicated(group)) + + # axes labels ----------------------------- + + resVsPredPlotConfiguration <- .updatePlotConfigurationAxesLabels(pairedData, resVsPredPlotConfiguration) + + # plot ----------------------------- + + tlf::setDefaultErrorbarCapSize(defaultPlotConfiguration$errorbarsCapSize) + + tlf::plotResVsPred( + data = as.data.frame(pairedData), + dataMapping = tlf::ResVsPredDataMapping$new( + x = "yValuesSimulated", + y = "residualValues", + group = "group", + shape = "name" + ), + plotConfiguration = resVsPredPlotConfiguration + ) + ggplot2::guides( + shape = "none", + col = ggplot2::guide_legend( + title = resVsPredPlotConfiguration$legend$title$text, + title.theme = resVsPredPlotConfiguration$legend$title$createPlotFont(), + override.aes = list(shape = overrideShapeAssignment$shapeAssn) + ) + ) +} diff --git a/R/plot-residuals-vs-time.R b/R/plot-residuals-vs-time.R new file mode 100644 index 000000000..d7d3f5efd --- /dev/null +++ b/R/plot-residuals-vs-time.R @@ -0,0 +1,126 @@ +#' Residuals versus time scatter plot +#' +#' @inheritParams plotIndividualTimeProfile +#' @inheritParams tlf::plotResVsTime +#' +#' @import tlf +#' +#' @family plotting +#' +#' @examples +#' # simulated data +#' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +#' sim <- loadSimulation(simFilePath) +#' simResults <- runSimulation(sim) +#' outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)" +#' +#' # observed data +#' obsData <- lapply( +#' c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"), +#' function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite")) +#' ) +#' names(obsData) <- lapply(obsData, function(x) x$name) +#' +#' +#' # Create a new instance of `DataCombined` class +#' myDataCombined <- DataCombined$new() +#' +#' # Add simulated results +#' myDataCombined$addSimulationResults( +#' simulationResults = simResults, +#' quantitiesOrPaths = outputPath, +#' groups = "Aciclovir PVB" +#' ) +#' +#' # Add observed data set +#' myDataCombined$addDataSets(obsData$`Vergin 1995.Iv`, groups = "Aciclovir PVB") +#' +#' # Create a new instance of `DefaultPlotConfiguration` class +#' myPlotConfiguration <- DefaultPlotConfiguration$new() +#' myPlotConfiguration$title <- "My Plot Title" +#' myPlotConfiguration$subtitle <- "My Plot Subtitle" +#' myPlotConfiguration$caption <- "My Sources" +#' +#' # plot +#' plotResidualsVsTime(myDataCombined, myPlotConfiguration) +#' +#' @export +plotResidualsVsTime <- function(dataCombined, + defaultPlotConfiguration = NULL) { + # validation ----------------------------- + + defaultPlotConfiguration <- .validateDefaultPlotConfiguration(defaultPlotConfiguration) + + .validateDataCombinedForPlotting(dataCombined) + if (is.null(dataCombined$groupMap)) { + return(NULL) + } + + # `ResVsTimePlotConfiguration` object ----------------------------- + + # Create an instance of plot-specific class object + resVsTimePlotConfiguration <- .convertGeneralToSpecificPlotConfiguration( + specificPlotConfiguration = tlf::ResVsTimePlotConfiguration$new(), + generalPlotConfiguration = defaultPlotConfiguration + ) + + # This should never be the case as the residuals should be centered around 0. + is_y_scale_logarithmic <- resVsTimePlotConfiguration$yAxis$scale == "log" + if (is_y_scale_logarithmic) { + stop(messages$logScaleNotAllowed()) + } + + # data frames ----------------------------- + + # Create a paired data frame (observed versus simulated) from `DataCombined` object. + # + # `DefaultPlotConfiguration` provides units for conversion. + # `PlotConfiguration` provides scaling details needed while computing residuals. + pairedData <- calculateResiduals(dataCombined, + scaling = resVsTimePlotConfiguration$yAxis$scale, + xUnit = defaultPlotConfiguration$xUnit, + yUnit = defaultPlotConfiguration$yUnit + ) + + # Quit early if there is no data to visualize. + if (is.null(pairedData)) { + return(NULL) + } + + # Since groups might include more than one observed dataset (indicated by shape) + # in a group (indicated by color), we have to override the default shape legend + # and assign a manual shape to each legend entry + # The shapes follow the settings in the user-provided plot configuration + overrideShapeAssignment <- pairedData %>% + dplyr::select(name, group) %>% + dplyr::distinct() %>% + dplyr::arrange(name) %>% + dplyr::mutate(shapeAssn = resVsTimePlotConfiguration$points$shape[1:nrow(.)]) %>% + dplyr::filter(!duplicated(group)) + + # axes labels ----------------------------- + + resVsTimePlotConfiguration <- .updatePlotConfigurationAxesLabels(pairedData, resVsTimePlotConfiguration) + + # plot ----------------------------- + + tlf::setDefaultErrorbarCapSize(defaultPlotConfiguration$errorbarsCapSize) + + tlf::plotResVsTime( + data = as.data.frame(pairedData), + dataMapping = tlf::ResVsTimeDataMapping$new( + x = "xValues", + y = "residualValues", + group = "group", + shape = "name" + ), + plotConfiguration = resVsTimePlotConfiguration + ) + ggplot2::guides( + shape = "none", + col = ggplot2::guide_legend( + title = resVsTimePlotConfiguration$legend$title$text, + title.theme = resVsTimePlotConfiguration$legend$title$createPlotFont(), + override.aes = list(shape = overrideShapeAssignment$shapeAssn) + ) + ) +} diff --git a/R/population.R b/R/population.R index 9add715ea..a73635513 100644 --- a/R/population.R +++ b/R/population.R @@ -67,7 +67,7 @@ Population <- R6::R6Class( #' Returns all values defined in the population the individual with id `individualId` #' @param individualId Id of individual for which all values should be returned getParameterValuesForIndividual = function(individualId) { - parameterValueListFrom(rClr::clrCall(self$ref, "AllParameterValuesForIndividual", as.integer(individualId))) + .parameterValueListFrom(rClr::clrCall(self$ref, "AllParameterValuesForIndividual", as.integer(individualId))) }, #' @description #' Removes the value of a parameter by path diff --git a/R/quantity.R b/R/quantity.R index 00e965620..2d6bece31 100644 --- a/R/quantity.R +++ b/R/quantity.R @@ -129,7 +129,7 @@ Quantity <- R6::R6Class( validateIsString(unit, nullAllowed = TRUE) if (!is.null(unit)) { unit <- .encodeUnit(unit) - validateHasUnit(self, unit) + .validateHasUnit(self, unit) value <- rClr::clrCallStatic(WITH_DIMENSION_EXTENSION, "ConvertToBaseUnit", self$ref, value, unit) } self$value <- value diff --git a/R/simulation-batch.R b/R/simulation-batch.R index 6167418b4..2b0f25eec 100644 --- a/R/simulation-batch.R +++ b/R/simulation-batch.R @@ -31,7 +31,8 @@ SimulationBatch <- R6::R6Class( #' @description Add a set of parameter and start values for next execution. #' @details Intended for the use with `runSimulationBatches`. The simulation batch is executed - #' with the sets of parameter and initial values that have been scheduled. The set of run values is cleared after successful run. + #' with the sets of parameter and initial values that have been scheduled. + #' The set of run values is cleared after successful run. #' #' @param parameterValues Vector of parameter values to set in the simulation (default is `NULL`) #' @param initialValues Vector of initial values to set in the simulation (default is `NULL`) @@ -68,28 +69,62 @@ SimulationBatch <- R6::R6Class( stop(messages$errorOnlyOneValuesSetAllowed("parameterValues, initialValues")) } + # Check if any of the values is `NA`. If so, throw an error, as such + # values set will produce empty results + if (any(is.na(parameterValues))) { + naIdx <- which(is.na(parameterValues)) + stop(messages$simBatchStartValueNaN(self$getVariableParameters()[naIdx])) + } + if (any(is.na(initialValues))) { + naIdx <- which(is.na(initialValues)) + stop(messages$simBatchStartValueNaN(self$getVariableMolecules()[naIdx])) + } + batchRunValues <- SimulationBatchRunValues$new(parameterValues, initialValues) rClr::clrCall(self$ref, "AddSimulationBatchRunValues", batchRunValues$ref) return(batchRunValues$id) }, + #' @description Returns a list of parameter paths that are variable in this batch. + #' @details The order of parameters is the same as the order of parameter + #' values added with `$addRunValues()` method. + #' + #' @return List of parameter paths, or `NULL` if no parameter is variable. + #' @export + getVariableParameters = function() { + simulationBatchOptions <- rClr::clrGet(self$ref, "SimulationBatchOptions") + + rClr::clrGet(simulationBatchOptions, "VariableParameters") %||% + rClr::clrGet(simulationBatchOptions, "VariableParameter") + }, + + #' @description Returns a list of molecules paths that are variable in this batch + #' + #' @details The order of molecules is the same as the order of molecule + #' start values added with `$addRunValues()` method. + #' + #' @return List of parameter paths, or `NULL` if no molecule is variable. + #' @export + getVariableMolecules = function() { + simulationBatchOptions <- rClr::clrGet(self$ref, "SimulationBatchOptions") + + rClr::clrGet(simulationBatchOptions, "VariableMolecules") %||% + rClr::clrGet(simulationBatchOptions, "VariableMolecule") + }, + #' @description #' Print the object to the console #' @param ... Additional arguments. print = function(...) { - simulationBatchOptions <- rClr::clrGet(self$ref, "SimulationBatchOptions") private$printClass() + private$printLine("Id", self$id) private$printLine("Simulation", self$simulation$name) private$printLine("runValuesIds", self$runValuesIds) private$printLine( - "Parameters", - rClr::clrGet(simulationBatchOptions, "VariableParameters") %||% - rClr::clrGet(simulationBatchOptions, "VariableParameter") + "Parameters", self$getVariableParameters() ) private$printLine( - "Molecules", - rClr::clrGet(simulationBatchOptions, "VariableMolecules") %||% - rClr::clrGet(simulationBatchOptions, "VariableMolecule") + "Molecules", self$getVariableMolecules() ) invisible(self) } @@ -110,6 +145,10 @@ SimulationBatch <- R6::R6Class( } else { private$throwPropertyIsReadonly("runValuesIds") } + }, + #' @field id The id of the .NET wrapped object. (read-only) + id = function(value) { + private$wrapReadOnlyProperty("Id", value) } ), ) diff --git a/R/simulation.R b/R/simulation.R index 55ee1ce46..6fccefc85 100644 --- a/R/simulation.R +++ b/R/simulation.R @@ -61,7 +61,7 @@ Simulation <- R6::R6Class( rClr::clrCall(private$.buildConfiguration, "AllPresentEndogenousStationaryMoleculeNames") }, #' @description - #' Returns the name of all xenobiotoc floating molecules defined in the simulation. (e.g. with the flag IsStationary = FALSE) + #' Returns the name of all xenobiotic floating molecules defined in the simulation. (e.g. with the flag IsStationary = FALSE) #' This is typically a molecule that is being explicitly simulated such as Compound, Inhibitor, DrugComplex. allXenobioticFloatingMoleculeNames = function() { rClr::clrCall(private$.buildConfiguration, "AllPresentXenobioticFloatingMoleculeNames") diff --git a/R/table-formula.R b/R/table-formula.R index e59ac16fe..295f298f6 100644 --- a/R/table-formula.R +++ b/R/table-formula.R @@ -8,7 +8,7 @@ TableFormula <- R6::R6Class( cloneable = FALSE, inherit = Formula, active = list( - #' @field allPoints Returns all points defined in the table formulafor a `TableFormula` or `NULL` otherwise (Read-Only). + #' @field allPoints Returns all points defined in the table formula for a `TableFormula` or `NULL` otherwise (Read-Only). allPoints = function(value) { if (missing(value)) { .toObjectType(rClr::clrCall(self$ref, "AllPointsAsArray"), ValuePoint) diff --git a/R/user-defined-pk-parameter.R b/R/user-defined-pk-parameter.R index 96ebcb9c2..efc0044c5 100644 --- a/R/user-defined-pk-parameter.R +++ b/R/user-defined-pk-parameter.R @@ -35,7 +35,7 @@ UserDefinedPKParameter <- R6::R6Class("UserDefinedPKParameter", endApplicationIndex = function(value) { private$wrapIndexProperty("EndApplicationIndex", value) }, - #' @field normalizationFactor Factor to use to normalized the calculated PK-Parameter. (typically DrugMass, Dose, Dose per bodyweight). + #' @field normalizationFactor Factor to use to normalized the calculated PK-Parameter. (typically DrugMass, Dose, DosePerBodyWeight). #' It is the responsibility of the caller to ensure that the value is in the correct unit. (optional) normalizationFactor = function(value) { private$wrapProperty("NormalizationFactor", value) diff --git a/R/utilities-data-combined.R b/R/utilities-data-combined.R index 247825472..e925d0bcc 100644 --- a/R/utilities-data-combined.R +++ b/R/utilities-data-combined.R @@ -1,4 +1,359 @@ -#' Validate arguments provided as vectors +#' Convert datasets in `DataCombined` to common units +#' +#' @description +#' +#' When multiple (observed and/or simulated) datasets are present in a data +#' frame, they are likely to have different units. This function helps to +#' convert them to a common unit specified by the user. +#' +#' This is especially helpful while plotting since the quantities from different +#' datasets to be plotted on the X-and Y-axis need to have same units to be +#' meaningfully compared. +#' +#' @note +#' +#' Molecular weight is **required** for the conversion between certain +#' dimensions (`Amount`, `Mass`, `Concentration (molar)`, and `Concentration +#' (mass)`). Therefore, if molecular weight is missing for these dimension, the +#' unit conversion will fail. +#' +#' @return A data frame with measurement columns transformed to have common units. +#' +#' @param dataCombined A single instance of `DataCombined` class. +#' @param xUnit,yUnit Target units for `xValues` and `yValues`, respectively. If +#' not specified (`NULL`), first of the existing units in the respective +#' columns (`xUnit` and `yUnit`) will be selected as the common unit. For +#' available dimensions and units, see `ospsuite::ospDimensions` and +#' `ospsuite::ospUnits`, respectively. +#' +#' @return +#' +#' In the returned tibble data frame, the following columns will always be present: +#' +#' name - group - dataType - xValues - xDimension - xUnit - yValues - +#' yErrorValues - yDimension - yUnit - yErrorType - yErrorUnit - molWeight +#' +#' Importantly, the `xUnit` and `yUnit` columns will have unique entries. +#' +#' @family data-combined +#' +#' @examples +#' # simulated data +#' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +#' sim <- loadSimulation(simFilePath) +#' simResults <- runSimulation(sim) +#' outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)" +#' +#' # observed data +#' obsData <- lapply( +#' c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"), +#' function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite")) +#' ) +#' names(obsData) <- lapply(obsData, function(x) x$name) +#' +#' +#' # Create a new instance of `DataCombined` class +#' myDataCombined <- DataCombined$new() +#' +#' # Add simulated results +#' myDataCombined$addSimulationResults( +#' simulationResults = simResults, +#' quantitiesOrPaths = outputPath, +#' groups = "Aciclovir PVB" +#' ) +#' +#' # Add observed data set +#' myDataCombined$addDataSets(obsData$`Vergin 1995.Iv`, groups = "Aciclovir PVB") +#' +#' convertUnits( +#' myDataCombined, +#' xUnit = ospUnits$Time$s, +#' yUnit = ospUnits$`Concentration [mass]`$`µg/l` +#' ) +#' +#' @export +convertUnits <- function(dataCombined, xUnit = NULL, yUnit = NULL) { + .validateScalarDataCombined(dataCombined) + + # Extract combined data frame + combinedData <- dataCombined$toDataFrame() + + # Getting all units on the same scale + combinedData <- .unitConverter(combinedData, xUnit, yUnit) + + return(combinedData) +} + +#' Calculate residuals for datasets in `DataCombined` +#' +#' @details +#' +#' To compute residuals, for every simulated dataset in a given group, there +#' should also be a corresponding observed dataset. If this is not the case, the +#' corresponding observed or simulated datasets will be removed. +#' +#' When multiple (observed and/or simulated) datasets are present in +#' `DataCombined`, they are likely to have different units. The `xUnit` and +#' `yUnit` arguments help you specify a common unit to convert them to. +#' +#' @param scaling A character specifying scale: either `tlf::Scaling$lin` +#' (linear) or `tlf::Scaling$log` (logarithmic). +#' @inheritParams convertUnits +#' +#' @return +#' +#' In the returned tibble data frame, the following columns will always be present: +#' +#' xValues - xUnit - xDimension - yValuesObserved - yUnit - yDimension - +#' yErrorValues - yErrorType - yErrorUnit - yValuesSimulated - residualValues +#' +#' @family data-combined +#' +#' @examples +#' # simulated data +#' simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +#' sim <- loadSimulation(simFilePath) +#' simResults <- runSimulation(sim) +#' outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)" +#' +#' # observed data +#' obsData <- lapply( +#' c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"), +#' function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite")) +#' ) +#' names(obsData) <- lapply(obsData, function(x) x$name) +#' +#' +#' # Create a new instance of `DataCombined` class +#' myDataCombined <- DataCombined$new() +#' +#' # Add simulated results +#' myDataCombined$addSimulationResults( +#' simulationResults = simResults, +#' quantitiesOrPaths = outputPath, +#' groups = "Aciclovir PVB" +#' ) +#' +#' # Add observed data set +#' myDataCombined$addDataSets(obsData$`Vergin 1995.Iv`, groups = "Aciclovir PVB") +#' +#' calculateResiduals(myDataCombined, scaling = tlf::Scaling$lin) +#' @export +calculateResiduals <- function(dataCombined, + scaling, + xUnit = NULL, + yUnit = NULL) { + .validateScalarDataCombined(dataCombined) + + # Validation has already taken place in the calling plotting function + combinedData <- dataCombined$toDataFrame() + + # Remove the observed and simulated datasets which can't be paired. + combinedData <- .removeUnpairableDatasets(combinedData) + + # Return early if there are no pair-able datasets present + if (nrow(combinedData) == 0L) { + warning(messages$residualsCanNotBeComputed()) + return(NULL) + } + + # Getting all datasets to have the same units. + combinedData <- .unitConverter(combinedData, xUnit, yUnit) + + # Create observed versus simulated paired data using interpolation for each + # grouping level and combine the resulting data frames in a row-wise manner. + # + # Both of these routines will be carried out by `dplyr::group_modify()`. + pairedData <- combinedData %>% + dplyr::group_by(group) %>% + dplyr::group_modify(.f = ~ .extractResidualsToTibble(.x, scaling)) %>% + dplyr::ungroup() + + return(pairedData) +} + +#' Created observed versus simulated paired data +#' +#' @param data A data frame from `DataCombined$toDataFrame()`, which has been +#' further tidied using `.removeUnpairableDatasets()` and then +#' `.unitConverter()` functions. +#' +#' @keywords internal +.extractResidualsToTibble <- function(data, scaling) { + # Since the data frames will be fed to `matrix()`, make sure that data has + # `data.frame` class. That is, if tibbles are supplied, coerce them to a + # simple data frame. + observedData <- as.data.frame(dplyr::filter(data, dataType == "observed")) + simulatedData <- as.data.frame(dplyr::filter(data, dataType == "simulated")) + + # If available, error values will be useful for plotting error bars in the + # scatter plot. Even if not available, add missing values to be consistent. + if ("yErrorValues" %in% colnames(data)) { + yErrorValues <- data$yErrorValues[data$dataType == "observed"] + } else { + yErrorValues <- rep(NA_real_, nrow(observedData)) + } + + # Most of the columns in the observed data frame should also be included in + # the paired data frame for completeness. + pairedData <- dplyr::select( + observedData, + # Identifier column + name, + # Everything related to the X-variable + "xValues", "xUnit", "xDimension", dplyr::matches("^x"), + # Everything related to the Y-variable + "yValuesObserved" = "yValues", "yUnit", "yDimension", dplyr::matches("^y"), + # lower limit of quantification + "lloq" + ) + + # Add predicted values + # the approx function with a default rule = 1 argument returns NA for extrapolated points + pairedData <- dplyr::mutate(pairedData, + "yValuesSimulated" = approx( + simulatedData$xValues, simulatedData$yValues, + observedData$xValues + )$y + ) + # Residual computation will depend on the scaling. + if (scaling %in% c(tlf::Scaling$lin, tlf::Scaling$identity)) { + pairedData <- dplyr::mutate(pairedData, residualValues = yValuesSimulated - yValuesObserved) + } else { + # Epsilon for safe log calculation should be converted to the units of the values + epsilon <- toUnit( + quantityOrDimension = pairedData$yDimension[[1]], + values = ospsuiteEnv$LOG_SAFE_EPSILON, + targetUnit = pairedData$yUnit[[1]], + molWeight = 1 + ) + pairedData <- dplyr::mutate(pairedData, residualValues = .log_safe(yValuesSimulated, epsilon = epsilon) - .log_safe(yValuesObserved, epsilon = epsilon)) + } + + # some residual values might turn out to be NA (for example, when extrapolating) + # they are not returned in the output tibble + pairedData <- dplyr::filter( + pairedData, + !is.na(residualValues) + ) + + return(pairedData) +} + +# TODO: +# +# Depending on what is decided in issue +# https://github.com/Open-Systems-Pharmacology/OSPSuite-R/issues/1091, change +# defaults for `base` for `.log_safe`. + +#' @keywords internal +#' @noRd +.log_safe <- function(x, base = 10, epsilon = ospsuiteEnv$LOG_SAFE_EPSILON) { + x <- sapply(X = x, function(element){ + element <- ospsuite.utils::toMissingOfType(element, type = "double") + if (is.na(element)) { + return(NA_real_) + } else if (element < epsilon) { + return(log(epsilon, base = base)) + } else { + return(log(element, base = base)) + } + }) + + return(x) +} + +#' Remove unpairable datasets for computing residuals +#' +#' @description +#' +#' Computing residuals by definition requires that data should be in pairs, i.e. +#' for every simulated dataset in a given group, there should also be a +#' corresponding observed dataset. +#' +#' To this end, current function removes the following datasets: +#' +#' - Datasets which haven't been assigned to any group. +#' - Datasets that are not part of a pair (i.e. a simulated dataset without +#' observed dataset partner, and vice versa). +#' +#' @param data A data frame returned by `DataCombined$toDataFrame()`. +#' +#' @examples +#' +#' df <- dplyr::tribble( +#' ~name, ~dataType, ~group, +#' "Sim1", "Simulated", "GroupA", +#' "Sim2", "Simulated", "GroupA", +#' "Obs1", "Observed", "GroupB", +#' "Obs2", "Observed", "GroupB", +#' "Sim3", "Simulated", "GroupC", +#' "Obs3", "Observed", "GroupC", +#' "Sim4", "Simulated", "GroupD", +#' "Obs4", "Observed", "GroupD", +#' "Obs5", "Observed", "GroupD", +#' "Sim5", "Simulated", "GroupE", +#' "Sim6", "Simulated", "GroupE", +#' "Obs7", "Observed", "GroupE", +#' "Sim7", "Simulated", "GroupF", +#' "Sim8", "Simulated", "GroupF", +#' "Obs8", "Observed", "GroupF", +#' "Obs9", "Observed", "GroupF", +#' "Sim9", "Simulated", NA, +#' "Obs10", "Observed", NA +#' ) +#' +#' # original +#' df +#' +#' # transformed +#' ospsuite:::.removeUnpairableDatasets(df) +#' +#' @keywords internal +.removeUnpairableDatasets <- function(data) { + # How many rows were originally present? + originalDatasets <- unique(data$name) + + # Remove datasets that don't belong to any group. + data <- dplyr::filter(data, !is.na(group)) + + # Remove groups (and the datasets therein) with only one type (either only + # observed or only simulated) of dataset. + data <- data %>% + dplyr::group_by(group) %>% + dplyr::filter(length(unique(dataType)) > 1L) %>% + dplyr::ungroup() + + # How many rows are left after filtering? + finalDatasets <- unique(data$name) + + # Inform the user about which (if any) datasets were removed. + if (length(finalDatasets) < length(originalDatasets)) { + missingDatasets <- originalDatasets[!originalDatasets %in% finalDatasets] + + message(messages$printMultipleEntries( + header = messages$datasetsToGroupNotFound(), + entries = missingDatasets + )) + } + + return(data) +} + +#' Validate that single instance of `DataCombined` +#' +#' @examples +#' ospsuite:::.validateScalarDataCombined(DataCombined$new()) # okay +#' # ospsuite:::.validateScalarDataCombined(list(DataCombined$new(), DataCombined$new())) # error +#' +#' @keywords internal +.validateScalarDataCombined <- function(dataCombined) { + validateIsOfType(dataCombined, "DataCombined") + validateIsSameLength(objectCount(dataCombined), 1L) +} + + +#' Validate arguments provided as vectors #' #' @details #' @@ -16,15 +371,11 @@ #' @inheritParams ospsuite.utils::validateIsOfType #' #' @return -#' #' An atomic vector of desired data type. #' #' @examples -#' #' ospsuite:::.cleanVectorArgs(list(1, 2, NA, NULL), 4L, "numeric") -#' ospsuite:::.cleanVectorArgs(c(1, 2, NA, NA_complex), 4L, "numeric") #' @keywords internal -#' @noRd .cleanVectorArgs <- function(arg = NULL, expectedLength = NULL, type) { # Return early if no argument was specified if (is.null(arg)) { @@ -42,7 +393,7 @@ # convert `NULL`s or logical `NA`s to `NA` of required type # Note that `purrr::map()` will return a list - arg <- purrr::map(arg, function(x) .toMissingOfType(x, type)) + arg <- purrr::map(arg, function(x) toMissingOfType(x, type)) # validate the type of arguments @@ -52,75 +403,7 @@ # arguments are still in a list # flatten them to an atomic vector of required type - arg <- .flattenList(arg, type) + arg <- flattenList(arg, type) return(arg) } - -#' Flatten a list to an atomic vector of desired type -#' -#' @param x A list or an atomic vector. If the latter, no change will be made. -#' @param type Type of atomic vector to be returned. -#' -#' @details -#' -#' The `type` argument will decide which variant from `purrr::flatten()` family -#' is used to flatten the list. -#' -#' @examples -#' -#' ospsuite:::.flattenList(list(1, 2, 3, NA), type = "numeric") -#' ospsuite:::.flattenList(list(TRUE, FALSE, NA), type = "integer") -#' @return An atomic vector of desired type. -#' -#' @keywords internal -#' @noRd -.flattenList <- function(x, type) { - if (!is.null(dim(x))) { - stop("Argument to parameter `x` can only be a vector.") - } - - if (is.list(x)) { - x <- switch(type, - "character" = purrr::flatten_chr(x), - "numeric" = , - "real" = , - "double" = purrr::flatten_dbl(x), - "integer" = purrr::flatten_int(x), - "logical" = purrr::flatten_lgl(x), - purrr::flatten(x) - ) - } - - return(x) -} - - -#' Convert `NULL` or `NA`s to `NA` of desired type -#' -#' @param x A single element. -#' @inheritParams .flattenList -#' -#' @examples -#' -#' ospsuite:::.toMissingOfType(NA, type = "real") -#' ospsuite:::.toMissingOfType(NULL, type = "integer") -#' @keywords internal -#' @noRd -.toMissingOfType <- function(x, type) { - # all unexpected values will be converted to `NA` of a desired type - if (is.null(x) || is.na(x) || is.nan(x) || is.infinite(x)) { - x <- switch(type, - "character" = NA_character_, - "numeric" = , - "real" = , - "double" = NA_real_, - "integer" = NA_integer_, - "complex" = NA_complex_, - "logical" = NA, - stop("Incorrect type entered.") - ) - } - - return(x) -} diff --git a/R/utilities-data-repository.R b/R/utilities-data-repository.R index 7030c060e..e90e272cf 100644 --- a/R/utilities-data-repository.R +++ b/R/utilities-data-repository.R @@ -5,7 +5,7 @@ #' @keywords internal .loadDataRepositoryFromPKML <- function(filePath) { validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) dataRepositoryTask <- .getNetTask("DataRepositoryTask") dataRepository <- rClr::clrCall(dataRepositoryTask, "LoadDataRepository", filePath) DataRepository$new(dataRepository) diff --git a/R/utilities-data-set.R b/R/utilities-data-set.R index ef74a53d6..358c5cd8e 100644 --- a/R/utilities-data-set.R +++ b/R/utilities-data-set.R @@ -56,7 +56,7 @@ loadDataSetFromPKML <- function(filePath) { saveDataSetToPKML <- function(dataSet, filePath) { validateIsString(filePath) validateIsOfType(dataSet, "DataSet") - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) dataRepositoryTask <- .getNetTask("DataRepositoryTask") rClr::clrCall(dataRepositoryTask, "SaveDataRepository", dataSet$dataRepository$ref, filePath) } diff --git a/R/utilities-dot-net.R b/R/utilities-dot-net.R index 307cc893c..754bc0a7e 100644 --- a/R/utilities-dot-net.R +++ b/R/utilities-dot-net.R @@ -4,8 +4,8 @@ #' @param netObjects List of .NET object #' @param propertyName Property name that should be retrieved from the `netObjects` #' @keywords internal -getPropertyValues <- function(netObjects, propertyName) { - sapply(netObjects, function(x) getPropertyValue(x, propertyName)) +.getPropertyValues <- function(netObjects, propertyName) { + sapply(netObjects, function(x) .getPropertyValue(x, propertyName)) } #' Returns the value of property named `propertyName` from .NET object instance `netObject` @@ -13,6 +13,6 @@ getPropertyValues <- function(netObjects, propertyName) { #' @param netObject .NET object #' @param propertyName Property name that should be retrieved from the `netObject` #' @keywords internal -getPropertyValue <- function(netObject, propertyName) { +.getPropertyValue <- function(netObject, propertyName) { rClr::clrGet(netObject, name = propertyName) } diff --git a/R/utilities-file.R b/R/utilities-file.R index 72f88fbe7..72b2a3e64 100644 --- a/R/utilities-file.R +++ b/R/utilities-file.R @@ -37,6 +37,6 @@ #' Returns the expanded path for `path` and ensure that encoding is applied properly #' #' @param path Path to expand -expandPath <- function(path) { +.expandPath <- function(path) { path.expand(enc2utf8(path)) } diff --git a/R/utilities-individual.R b/R/utilities-individual.R index c0faef695..c40271f63 100644 --- a/R/utilities-individual.R +++ b/R/utilities-individual.R @@ -1,4 +1,4 @@ -#' Creates an individual using the PKSim Database +#' Creates an individual using the PK-Sim Database #' #' @param individualCharacteristics Characteristics of the individual to create #' as an instance of `IndividualCharacteristics` @@ -19,12 +19,12 @@ createIndividual <- function(individualCharacteristics) { individualFactory <- rClr::clrCallStatic("PKSim.R.Api", "GetIndividualFactory") createIndividualResults <- rClr::clrCall(individualFactory, "CreateIndividual", individualCharacteristics$ref) - distributedParameters <- getPropertyValue(createIndividualResults, "DistributedParameters") - derivedParameters <- getPropertyValue(createIndividualResults, "DerivedParameters") - seed <- getPropertyValue(createIndividualResults, "Seed") + distributedParameters <- .getPropertyValue(createIndividualResults, "DistributedParameters") + derivedParameters <- .getPropertyValue(createIndividualResults, "DerivedParameters") + seed <- .getPropertyValue(createIndividualResults, "Seed") - distributedParameters <- parameterValueListFrom(distributedParameters, addUnits = TRUE) - derivedParameters <- parameterValueListFrom(derivedParameters, addUnits = TRUE) + distributedParameters <- .parameterValueListFrom(distributedParameters, addUnits = TRUE) + derivedParameters <- .parameterValueListFrom(derivedParameters, addUnits = TRUE) list(distributedParameters = distributedParameters, derivedParameters = derivedParameters, seed = seed) } @@ -43,16 +43,16 @@ createDistributions <- function(individualCharacteristics) { distributedParameters <- rClr::clrCall(individualFactory, "DistributionsFor", individualCharacteristics$ref) list( - paths = getPropertyValues(distributedParameters, "ParameterPath"), - values = getPropertyValues(distributedParameters, "Value"), - units = getPropertyValues(distributedParameters, "Unit"), - means = getPropertyValues(distributedParameters, "Mean"), - stds = getPropertyValues(distributedParameters, "Std"), - distributionTypes = getPropertyValues(getPropertyValues(distributedParameters, "DistributionType"), "DisplayName") + paths = .getPropertyValues(distributedParameters, "ParameterPath"), + values = .getPropertyValues(distributedParameters, "Value"), + units = .getPropertyValues(distributedParameters, "Unit"), + means = .getPropertyValues(distributedParameters, "Mean"), + stds = .getPropertyValues(distributedParameters, "Std"), + distributionTypes = .getPropertyValues(.getPropertyValues(distributedParameters, "DistributionType"), "DisplayName") ) } -#' Creates an individual using the PKSim Database. +#' Creates an individual using the PK-Sim Database. #' #' @param species Species of the individual as defined in PK-Sim (see Species enum) #' @param population Population to use to create the individual. This is required only when the species is Human. (See HumanPopulation enum) @@ -89,7 +89,6 @@ createIndividualCharacteristics <- function(species, gestationalAgeUnit = "week(s)", moleculeOntogenies = NULL, seed = NULL) { - # Assuming that if this function is called directly, PK-Sim was either initialized already # or should be initialized automatically initPKSim() diff --git a/R/utilities-molecule.R b/R/utilities-molecule.R index abd078a93..8378cbcb1 100644 --- a/R/utilities-molecule.R +++ b/R/utilities-molecule.R @@ -73,7 +73,7 @@ getMolecule <- function(path, container, stopIfNotFound = TRUE) { #' @param values A numeric value that should be assigned to the molecule start value or a vector #' of numeric values, if the start value of more than one molecule should be changed. Must have the same #' length as `molecules` -#' @inheritParams setQuantityValues +#' @inheritParams .setQuantityValues #' #' @examples #' @@ -86,7 +86,7 @@ getMolecule <- function(path, container, stopIfNotFound = TRUE) { #' @export setMoleculeInitialValues <- function(molecules, values, units = NULL) { validateIsOfType(molecules, "Molecule") - setQuantityValues(molecules, values, units) + .setQuantityValues(molecules, values, units) } diff --git a/R/utilities-parameter-value.R b/R/utilities-parameter-value.R index 89edfcc23..0a2e369fc 100644 --- a/R/utilities-parameter-value.R +++ b/R/utilities-parameter-value.R @@ -7,14 +7,14 @@ #' @param addUnits If `TRUE`, a a third list will be returned containing the units in which the parameters are defined. Default is `FALSE` #' #' @return A list with 3 sublist: `paths`, `values`, and optionally `units` containing the corresponding values from each parameter value -parameterValueListFrom <- function(netParameterValues, addUnits = FALSE) { +.parameterValueListFrom <- function(netParameterValues, addUnits = FALSE) { parameterList <- list( - paths = getPropertyValues(netParameterValues, "ParameterPath"), - values = getPropertyValues(netParameterValues, "Value") + paths = .getPropertyValues(netParameterValues, "ParameterPath"), + values = .getPropertyValues(netParameterValues, "Value") ) if (addUnits) { - parameterList$units <- getPropertyValues(netParameterValues, "Unit") + parameterList$units <- .getPropertyValues(netParameterValues, "Unit") } return(parameterList) diff --git a/R/utilities-parameter.R b/R/utilities-parameter.R index 6eccad102..cc82e809a 100644 --- a/R/utilities-parameter.R +++ b/R/utilities-parameter.R @@ -77,7 +77,7 @@ getParameter <- function(path, container, stopIfNotFound = TRUE) { #' displayPath <- getParameterDisplayPaths("Organism|Liver|Volume", sim) #' @export getParameterDisplayPaths <- function(paths, simulation) { - getQuantityDisplayPaths(paths, simulation) + .getQuantityDisplayPaths(paths, simulation) } #' Set values of parameters @@ -88,7 +88,7 @@ getParameterDisplayPaths <- function(paths, simulation) { #' @param values A numeric value that should be assigned to the parameter or a vector #' of numeric values, if the value of more than one parameter should be changed. Must have the same #' length as 'parameters'. Alternatively, the value can be a unique number. In that case, the same value will be set in all parameters -#' @inheritParams setQuantityValues +#' @inheritParams .setQuantityValues #' #' @examples #' @@ -101,7 +101,7 @@ getParameterDisplayPaths <- function(paths, simulation) { #' @export setParameterValues <- function(parameters, values, units = NULL) { validateIsOfType(parameters, "Parameter") - setQuantityValues(parameters, values, units) + .setQuantityValues(parameters, values, units) } @@ -152,5 +152,5 @@ setParameterValuesByPath <- function(parameterPaths, values, simulation, units = #' @export scaleParameterValues <- function(parameters, factor) { validateIsOfType(parameters, "Parameter") - scaleQuantityValues(parameters, factor) + .scaleQuantityValues(parameters, factor) } diff --git a/R/utilities-pk-analysis.R b/R/utilities-pk-analysis.R index 3d1935182..96be3e30d 100644 --- a/R/utilities-pk-analysis.R +++ b/R/utilities-pk-analysis.R @@ -33,14 +33,14 @@ calculatePKAnalyses <- function(results) { exportPKAnalysesToCSV <- function(pkAnalyses, filePath) { validateIsOfType(pkAnalyses, "SimulationPKAnalyses") validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) pkAnalysisTask <- .getNetTask("PKAnalysisTask") rClr::clrCall(pkAnalysisTask, "ExportPKAnalysesToCSV", pkAnalyses$ref, pkAnalyses$simulation$ref, filePath) invisible() } #' @inherit exportPKAnalysesToCSV -savePKAnalysesToCSV <- function(pkAnalyses, filePath) { +.savePKAnalysesToCSV <- function(pkAnalyses, filePath) { exportPKAnalysesToCSV(pkAnalyses, filePath) } @@ -54,7 +54,7 @@ savePKAnalysesToCSV <- function(pkAnalyses, filePath) { importPKAnalysesFromCSV <- function(filePath, simulation) { validateIsOfType(simulation, "Simulation") validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) pkAnalysisTask <- .getNetTask("PKAnalysisTask") pkAnalyses <- rClr::clrCall(pkAnalysisTask, "ImportPKAnalysesFromCSV", filePath, simulation$ref) SimulationPKAnalyses$new(pkAnalyses, simulation) diff --git a/R/utilities-pksim.R b/R/utilities-pksim.R index 7d8e0f6fc..e09b10565 100644 --- a/R/utilities-pksim.R +++ b/R/utilities-pksim.R @@ -1,5 +1,5 @@ -#' Loads the PKSim.R that will enable create individual and create population workflows. +#' Loads the `PKSim.R` that will enable create individual and create population workflows. #' @param pksimFolderPath Path where PK-Sim is installed. If this is not specified, path will be read from registry using the package version #' #' @note This will only work on Windows machine and should not be called on any other OS. @@ -8,7 +8,6 @@ #' @import rClr #' @export initPKSim <- function(pksimFolderPath = NULL) { - # pksimFolderPath <- "C:/dev/PK-Sim/src/PKSim/bin/Debug/net472" if (ospsuiteEnv$isPKSimLoaded) { @@ -39,7 +38,7 @@ initPKSim <- function(pksimFolderPath = NULL) { #' @param pksim.version The version number of Pk-Sim as a string. #' #' @return -#' The path to the PK-Sim installation for version pksim.version or NA if no path could be found. +#' The path to the PK-Sim installation for version `pksim.version` or NA if no path could be found. #' The path is separated with slashes (unix-style) and in compliance with base-R without a trailing slash. #' #' @examples @@ -74,16 +73,16 @@ initPKSim <- function(pksimFolderPath = NULL) { return(NA_character_) } -#' Tries to find the installation path for a specific version of PK-Sim via the filesystem. +#' Tries to find the installation path for a specific version of PK-Sim via the file system. #' Searching is done in the following order: -#' 1. Search via filesystem in a guessed installation folder from the base.search.folder -#' 3. Search via filesystem for PKSim.exe recursively from the defined base.search.folder (fallback) +#' 1. Search via file system in a guessed installation folder from the base.search.folder +#' 3. Search via file system for `PKSim.exe` recursively from the defined base.search.folder (fallback) #' #' @param pksim.version The version number of Pk-Sim as a string. -#' @param base.search.folder The base folder for filesystem-lookup fallback (default: 64-bit program folder) +#' @param base.search.folder The base folder for file system lookup fallback (default: 64-bit program folder) #' #' @return -#' The path to the PK-Sim installation for version pksim.version or NA if no path could be found. +#' The path to the PK-Sim installation for version `pksim.version` or NA if no path could be found. #' The path is separated with slashes (unix-style) and in compliance with base-R without a trailing slash. #' If more than one matching path is found a warning is produced. #' @@ -143,13 +142,13 @@ initPKSim <- function(pksimFolderPath = NULL) { #' Tries to find the installation path for a specific version of PK-Sim. #' Searching is done in the following order: #' 1. Search via Windows registry entry -#' 2. Search via filesystem in a guessed installation folder from the base.search.folder (fallback 1) +#' 2. Search via file system in a guessed installation folder from the base.search.folder (fallback 1) #' #' @param pksim.version The version number of Pk-Sim as a string. -#' @param base.search.folder The base folder for filesystem-lookup fallback (default: 64-bit program folder) +#' @param base.search.folder The base folder for file system lookup fallback (default: 64-bit program folder) #' #' @return -#' The path to the PK-Sim installation for version pksim.version or NA if no path could be found. +#' The path to the PK-Sim installation for version `pksim.version` or NA if no path could be found. #' The path is separated with slashes (unix-style) and in compliance with base-R without a trailing slash. #' If more than one matching path is found a warning is produced. #' diff --git a/R/utilities-plotting.R b/R/utilities-plotting.R index 2560065c9..4f38f253b 100644 --- a/R/utilities-plotting.R +++ b/R/utilities-plotting.R @@ -1,3 +1,34 @@ +#' Make sure entered `DataCombined` object is valid for plotting +#' +#' @family utilities-plotting +#' +#' @keywords internal +#' @noRd +.validateDataCombinedForPlotting <- function(dataCombined) { + .validateScalarDataCombined(dataCombined) + + # If there are no datasets in the object, no plot will be created. + if (is.null(dataCombined$groupMap)) { + warning(messages$plottingWithEmptyDataCombined()) + } +} + +#' Make sure entered `DefaultPlotConfiguration` object is valid for plotting +#' +#' @family utilities-plotting +#' +#' @keywords internal +#' @noRd +.validateDefaultPlotConfiguration <- function(defaultPlotConfiguration = NULL) { + defaultPlotConfiguration <- defaultPlotConfiguration %||% DefaultPlotConfiguration$new() + validateIsOfType(defaultPlotConfiguration, "DefaultPlotConfiguration") + + # Plotting functions should not update the configuration objects + defaultPlotConfiguration <- defaultPlotConfiguration$clone(deep = TRUE) + + return(defaultPlotConfiguration) +} + #' Replace missing groupings with dataset names #' #' @description @@ -7,6 +38,8 @@ #' #' @param data A data frame returned by `DataCombined$toDataFrame()`. #' +#' @family utilities-plotting +#' #' @examples #' #' df <- dplyr::tibble( @@ -54,106 +87,93 @@ return(data) } -#' Remove unpairable datasets for scatter plots + +#' Extract aggregated simulated data #' -#' @description +#' @param simData A data frame with simulated data from +#' `DataCombined$toDataFrame()`. +#' @inheritParams plotPopulationTimeProfile +#' +#' @details #' -#' Datasets which haven't been assigned to any group will be removed from the -#' combined data frame. +#' The simulated values will be aggregated across individuals for each time +#' point. #' -#' @param data A data frame returned by `DataCombined$toDataFrame()`. +#' @family utilities-plotting #' #' @examples #' -#' df <- dplyr::tribble( -#' ~name, ~dataType, ~group, -#' "Sim1", "Simulated", "GroupA", -#' "Sim2", "Simulated", "GroupA", -#' "Obs1", "Observed", "GroupB", -#' "Obs2", "Observed", "GroupB", -#' "Sim3", "Simulated", "GroupC", -#' "Obs3", "Observed", "GroupC", -#' "Sim4", "Simulated", "GroupD", -#' "Obs4", "Observed", "GroupD", -#' "Obs5", "Observed", "GroupD", -#' "Sim5", "Simulated", "GroupE", -#' "Sim6", "Simulated", "GroupE", -#' "Obs7", "Observed", "GroupE", -#' "Sim7", "Simulated", "GroupF", -#' "Sim8", "Simulated", "GroupF", -#' "Obs8", "Observed", "GroupF", -#' "Obs9", "Observed", "GroupF", -#' "Sim9", "Simulated", NA, -#' "Obs10", "Observed", NA +#' # let's create a data frame to test this function +#' df <- dplyr::tibble( +#' xValues = c( +#' 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, +#' 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, +#' 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5 +#' ), +#' yValues = c( +#' 0, +#' 0.990956723690033, 0.981773018836975, 0.972471475601196, 0.963047087192535, +#' 0.953498184680939, 0, 0.990953505039215, 0.981729507446289, 0.97233647108078, +#' 0.962786376476288, 0.953093528747559, 0, 0.990955889225006, 0.981753170490265, +#' 0.972399413585663, 0.962896287441254, 0.953253626823425, 0, 0.990950107574463, +#' 0.981710314750671, 0.972296476364136, 0.962724387645721, 0.953009009361267, +#' 0, 0.261394888162613, 0.266657412052155, 0.27151620388031, 0.275971591472626, +#' 0.280027687549591, 0, 0.26139160990715, 0.266613900661469, 0.271381109952927, +#' 0.275710910558701, 0.279623001813889, 0, 0.261393994092941, 0.266637593507767, +#' 0.271443992853165, 0.275820910930634, 0.279783099889755, 0, 0.261388212442398, +#' 0.266594797372818, 0.27134120464325, 0.275649011135101, 0.279538512229919 +#' ), +#' group = c(rep("Stevens 2012 solid total", 24), rep("Stevens 2012 solid distal", 24)), +#' name = group #' ) #' -#' # original +#' # raw data #' df #' -#' # transformed -#' ospsuite:::.removeUnpairableDatasets(df) -#' -#' @keywords internal -.removeUnpairableDatasets <- function(data) { - # How many rows were originally present - originalDatasets <- unique(data$name) - - # Remove datasets that don't belong to any group. - data <- dplyr::filter(data, !is.na(group)) - - # Remove groups (and the datasets therein) with only one type (either only - # observed or only simulated) of dataset. - data <- data %>% - dplyr::group_by(group) %>% - dplyr::filter(length(unique(dataType)) > 1L) %>% - dplyr::ungroup() - - # How many rows are present after filtering - finalDatasets <- unique(data$name) - - # Warn the user about the filtering if it took place - if (length(finalDatasets) < length(originalDatasets)) { - missingDatasets <- originalDatasets[!originalDatasets %in% finalDatasets] - - message(messages$printMultipleEntries( - header = messages$datasetsToGroupNotFound(), - entries = missingDatasets - )) - } - - return(data) -} - - -#' Extract aggregated simulated data +#' # aggregated data +#' ospsuite:::.extractAggregatedSimulatedData(df) #' #' @keywords internal -#' @noRd -.extractAggregatedSimulatedData <- function(simData, quantiles) { - # Compute quantiles +.extractAggregatedSimulatedData <- function(simData, + quantiles = c(0.05, 0.5, 0.95)) { simAggregatedData <- simData %>% - # For each dataset, compute across all individuals for each time point - dplyr::group_by(group, xValues) %>% # + # For each dataset, compute quantiles across all individuals for each time point + # + # Each group should always a single dataset, so grouping by `group` *and* `name` + # should produce the same result as grouping by only `group` column. + # + # The reason `name` column also needs to be retained in the resulting data + # is because it is mapped to linetype property in population profile type. + dplyr::group_by(group, name, xValues) %>% dplyr::summarise( - yValuesLower = stats::quantile(yValues, quantiles[[1]]), + yValuesLower = stats::quantile(yValues, quantiles[[1]]), yValuesCentral = stats::quantile(yValues, quantiles[[2]]), - yValuesHigher = stats::quantile(yValues, quantiles[[3]]), - .groups = "drop" # drop grouping information from the summary data frame - ) + yValuesHigher = stats::quantile(yValues, quantiles[[3]]), + .groups = "drop" # drop grouping information from the summary data frame + ) %>% # Naming schema expected by plotting functions + dplyr::rename(yValues = yValuesCentral) return(simAggregatedData) } #' Create axes labels #' +#' @details +#' +#' If axes labels haven't been specified, create them using information about +#' dimensions and units present in the data frame produced by +#' `DataCombined$toDataFrame()`. +#' #' @param data A data frame from `DataCombined$toDataFrame()`, which has -#' additionally been cleaned using `.unitConverter()` to have the same units -#' across datasets. +#' additionally been cleaned using `ospsuite:::.unitConverter()` to have the +#' same units across datasets. #' @param specificPlotConfiguration The nature of labels will change depending -#' on the type of plot, which can be guessed from the specific +#' on the type of plot. The type of plot can be guessed from the specific #' `PlotConfiguration` object used, since each plot has a unique corresponding #' class. #' +#' @family utilities-plotting +#' #' @examples #' #' df <- dplyr::tibble( @@ -172,10 +192,6 @@ #' #' ospsuite:::.createAxesLabels(df, tlf::TimeProfilePlotConfiguration$new()) #' -#' @details -#' -#' If axes labels haven't been specified, create them using dimensions and units. -#' #' @keywords internal .createAxesLabels <- function(data, specificPlotConfiguration) { # If empty data frame is entered or plot type is not specified, return early @@ -183,15 +199,40 @@ return(NULL) } - # The type of plot can be guessed from the specific `PlotConfiguration` object - # used, since each plot has a unique corresponding class. - plotType <- class(specificPlotConfiguration)[[1]] + # special concern for concentration -------------------------- + + # If there are multiple dimensions for Y-axis variable, it is most likely to + # be due to multiple concentration dimensions. + # + # Hard code these to a single dimension: `"Concentration"`. + # + # For more, see: + # https://github.com/Open-Systems-Pharmacology/OSPSuite-R/issues/938 + concDimensions <- c(ospDimensions$`Concentration (mass)`, ospDimensions$`Concentration (molar)`) + + if (!all(is.na(data$yDimension))) { + data <- dplyr::mutate(data, + yDimension = dplyr::case_when( + yDimension %in% concDimensions ~ "Concentration", + TRUE ~ yDimension + ) + ) + } + + if (!all(is.na(data$xDimension))) { + data <- dplyr::mutate(data, + xDimension = dplyr::case_when( + xDimension %in% concDimensions ~ "Concentration", + TRUE ~ xDimension + ) + ) + } # Initialize strings with unique values for units and dimensions. # # The`.unitConverter()` has already ensured that there is only a single unit - # for x and y quantities, so we can safely take the unique unit to prepare - # axes labels. + # for quantities, so we can safely take the unique unit to prepare axes + # labels. xUnitString <- unique(data$xUnit) yUnitString <- unique(data$yUnit) @@ -199,54 +240,38 @@ xDimensionString <- unique(data$xDimension)[[1]] yDimensionString <- unique(data$yDimension)[[1]] - # Currently, hard code any of the different concentration dimensions to just - # one dimension: "Concentration" - # - # https://github.com/Open-Systems-Pharmacology/OSPSuite-R/issues/938 - concDimensions <- c( - ospDimensions$`Concentration (mass)`, - ospDimensions$`Concentration (molar)` - ) - - if (any(xDimensionString %in% concDimensions)) { - xDimensionString <- "Concentration" - } - - if (any(yDimensionString %in% concDimensions)) { - yDimensionString <- "Concentration" - } - - # If quantities are unitless, no unit information will be displayed. - # Otherwise, `Dimension [Unit]` pattern will be followed. + # If quantities are unitless, no unit information needs to be displayed. + # Otherwise, `Dimension [Unit]` pattern is followed. xUnitString <- ifelse(xUnitString == "", xUnitString, paste0(" [", xUnitString, "]")) xUnitString <- paste0(xDimensionString, xUnitString) yUnitString <- ifelse(yUnitString == "", yUnitString, paste0(" [", yUnitString, "]")) yUnitString <- paste0(yDimensionString, yUnitString) # The exact axis label will depend on the type of the plot, and the type - # of the plot can be guessed using the specific `PlotConfiguration` object - # entered in this function. - # + # of the plot can be guessed using the specific `PlotConfiguration` object. + plotType <- class(specificPlotConfiguration)[[1]] + # If the specific `PlotConfiguration` object is not any of the cases included - # in the `switch` below, the result will be no change; i.e., the labels will - # continue to be `NULL`. + # in the `switch` below, the the labels will remain `NULL`. - # x-axis label + # X-axis label xLabel <- switch(plotType, - "TimeProfilePlotConfiguration" = xUnitString, - "ResVsPredPlotConfiguration" = xUnitString, + "TimeProfilePlotConfiguration" = , + "ResVsTimePlotConfiguration" = xUnitString, # Note that `yUnitString` here is deliberate. # # In case of an observed versus simulated plot, `yValues` are plotted on # both x- and y-axes, and therefore the units strings are going to be the # same for both axes. - "ObsVsPredPlotConfiguration" = paste0("Observed values (", yUnitString, ")") + "ObsVsPredPlotConfiguration" = paste0("Observed values (", yUnitString, ")"), + "ResVsPredPlotConfiguration" = paste0("Simulated values (", yUnitString, ")") ) - # y-axis label + # Y-axis label yLabel <- switch(plotType, "TimeProfilePlotConfiguration" = yUnitString, - "ResVsPredPlotConfiguration" = "Residuals", + "ResVsPredPlotConfiguration" = , + "ResVsTimePlotConfiguration" = "Residuals", "ObsVsPredPlotConfiguration" = paste0("Simulated values (", yUnitString, ")") ) @@ -254,54 +279,138 @@ } +#' Update axes label fields in `PlotConfiguration` object +#' +#' @family utilities-plotting +#' +#' @keywords internal +#' @noRd +.updatePlotConfigurationAxesLabels <- function(data, plotConfiguration) { + axesLabels <- .createAxesLabels(data, plotConfiguration) + + # Update only if the user hasn't already specified labels. + plotConfiguration$labels$xlabel$text <- plotConfiguration$labels$xlabel$text %||% axesLabels$xLabel + plotConfiguration$labels$ylabel$text <- plotConfiguration$labels$ylabel$text %||% axesLabels$yLabel + + return(plotConfiguration) +} + + +#' Compute error bar bounds from error type +#' +#' @keywords internal +#' @noRd +.computeBoundsFromErrorType <- function(data) { + if (is.null(data)) { + return(NULL) + } + + if (!all(is.na(data$yErrorValues)) && !all(is.na(data$yErrorType))) { + data <- dplyr::mutate(data, + # If the error values are 0, the error bar caps will be displayed even + # when there are no error bars. Replacing `0`s with `NA`s gets rid of this + # problem. + # + # For more, see: https://github.com/Open-Systems-Pharmacology/TLF-Library/issues/348 + yErrorValues = dplyr::case_when( + dplyr::near(yErrorValues, 0) ~ NA_real_, + TRUE ~ yErrorValues + ), + # For compuring uncertainty, there are only three possibilities: + # + # - The error type is arithmetic (`DataErrorType$ArithmeticStdDev`). + # - The error type is geometric (`DataErrorType$GeometricStdDev`). + # - If the errors are none of these, then add `NA`s (of type `double`), + # since these are the only error types supported in `DataErrorType`. + yValuesLower = dplyr::case_when( + yErrorType == DataErrorType$ArithmeticStdDev ~ yValues - yErrorValues, + yErrorType == DataErrorType$GeometricStdDev ~ yValues / yErrorValues, + TRUE ~ NA_real_ + ), + yValuesHigher = dplyr::case_when( + yErrorType == DataErrorType$ArithmeticStdDev ~ yValues + yErrorValues, + yErrorType == DataErrorType$GeometricStdDev ~ yValues * yErrorValues, + TRUE ~ NA_real_ + ) + ) + } else { + # These columns should always be present in the data frame because they are + # part of `{tlf}` mapping. + data <- dplyr::mutate(data, yValuesLower = NA_real_, yValuesHigher = NA_real_) + } + + return(data) +} + + + + + #' Create plot-specific `tlf::PlotConfiguration` object #' -#' @param data A data frame containing information about dimensions and units -#' for the x-and y-axes quantities. +#' @details +#' +#' The default plot configuration and the labels needs to vary from plot-to-plot +#' because each plot has its specific (default) aesthetic needs that need to be +#' met. +#' +#' For example, although the axes labels for profile plots will be (e.g.) "Time +#' vs Fraction", they will be (e.g.) "Observed vs simulated values" for scatter +#' plots. Additionally, mapping group to line colors might be desirable for a +#' profile plot, it is not so for scatter plots. +#' +#' This function generates object of specific subclass of +#' `tlf::PlotConfiguration` needed for the given plot but with suitable defaults +#' taken from the `DefaultPlotConfiguration` object. +#' #' @param specificPlotConfiguration A specific subclass of #' `tlf::PlotConfiguration` needed for the given plot. #' @param generalPlotConfiguration A `DefaultPlotConfiguration` object. #' +#' @family utilities-plotting +#' +#' @examples +#' +#' ospsuite:::.convertGeneralToSpecificPlotConfiguration( +#' tlf::TimeProfilePlotConfiguration$new(), +#' ospsuite::DefaultPlotConfiguration$new() +#' ) +#' #' @keywords internal -#' @noRd -.convertGeneralToSpecificPlotConfiguration <- function(data, - specificPlotConfiguration, +.convertGeneralToSpecificPlotConfiguration <- function(specificPlotConfiguration, generalPlotConfiguration) { - validateIsOfType(generalPlotConfiguration, "DefaultPlotConfiguration", nullAllowed = FALSE) - # Plot-specific configuration defaults ----------------------------------- - # The default plot configuration and the labels will vary from plot-to-plot. - # - # For example, although the axes labels for profile plots will be (e.g.) "Time - # vs Fraction", it will be "observed vs simulated values" with the same unit - # for scatter plot. Additionally, mapping group to line colors might be - # desirable for a profile plot, it is not so for scatter plots. - # The type of plot can be guessed from the specific `PlotConfiguration` object # used, since each plot has a unique corresponding class. plotType <- class(specificPlotConfiguration)[[1]] # For `plotIndividualTimeProfile()` and `plotPopulationTimeProfile()` if (plotType == "TimeProfilePlotConfiguration") { - generalPlotConfiguration$pointsColor <- generalPlotConfiguration$pointsColor %||% tlf::ColorMaps$ospDefault - generalPlotConfiguration$pointsShape <- generalPlotConfiguration$pointsShape %||% names(tlf::Shapes) - generalPlotConfiguration$linesColor <- generalPlotConfiguration$linesColor %||% tlf::ColorMaps$ospDefault - generalPlotConfiguration$linesLinetype <- generalPlotConfiguration$linesLinetype %||% tlf::Linetypes$solid - + # This is especially necessary when multiple simulated datasets are present per group + generalPlotConfiguration$linesLinetype <- generalPlotConfiguration$linesLinetype %||% names(tlf::Linetypes) generalPlotConfiguration$legendPosition <- generalPlotConfiguration$legendPosition %||% tlf::LegendPositions$insideTopRight + generalPlotConfiguration$xAxisScale <- generalPlotConfiguration$xAxisScale %||% tlf::Scaling$lin + generalPlotConfiguration$yAxisScale <- generalPlotConfiguration$yAxisScale %||% tlf::Scaling$lin } # For `plotObservedVsSimulated()` if (plotType == "ObsVsPredPlotConfiguration") { - generalPlotConfiguration$pointsColor <- generalPlotConfiguration$pointsColor %||% tlf::ColorMaps$ospDefault - generalPlotConfiguration$pointsShape <- generalPlotConfiguration$pointsShape %||% names(tlf::Shapes) + generalPlotConfiguration$linesColor <- generalPlotConfiguration$linesColor %||% "black" + generalPlotConfiguration$legendPosition <- generalPlotConfiguration$legendPosition %||% tlf::LegendPositions$insideBottomRight + generalPlotConfiguration$xAxisScale <- generalPlotConfiguration$xAxisScale %||% tlf::Scaling$log + generalPlotConfiguration$yAxisScale <- generalPlotConfiguration$yAxisScale %||% tlf::Scaling$log + # every fold distance line should get a unique type of line + generalPlotConfiguration$linesLinetype <- generalPlotConfiguration$linesLinetype %||% names(tlf::Linetypes) + } + # For `plotResidualsVsTime()` and `plotResidualsVsSimulated()` + if (plotType %in% c("ResVsTimePlotConfiguration", "ResVsPredPlotConfiguration")) { generalPlotConfiguration$linesColor <- generalPlotConfiguration$linesColor %||% "black" generalPlotConfiguration$linesLinetype <- generalPlotConfiguration$linesLinetype %||% tlf::Linetypes$dashed - - generalPlotConfiguration$legendPosition <- generalPlotConfiguration$legendPosition %||% tlf::LegendPositions$insideBottomRight + generalPlotConfiguration$xAxisScale <- generalPlotConfiguration$xAxisScale %||% tlf::Scaling$lin + generalPlotConfiguration$yAxisScale <- generalPlotConfiguration$yAxisScale %||% tlf::Scaling$lin } # labels object --------------------------------------- @@ -375,20 +484,25 @@ align = generalPlotConfiguration$legendTitleAlign ) - legendCaptionFont <- tlf::Font$new( - size = generalPlotConfiguration$legendCaptionSize, - color = generalPlotConfiguration$legendCaptionColor, - fontFamily = generalPlotConfiguration$legendCaptionFontFamily, - fontFace = generalPlotConfiguration$legendCaptionFontFace, - angle = generalPlotConfiguration$legendCaptionAngle, - align = generalPlotConfiguration$legendCaptionAlign + legendTitleLabel <- tlf::Label$new( + text = generalPlotConfiguration$legendTitle, + font = legendTitleFont + ) + + legendKeysFont <- tlf::Font$new( + size = generalPlotConfiguration$legendKeysSize, + color = generalPlotConfiguration$legendKeysColor, + fontFamily = generalPlotConfiguration$legendKeysFontFamily, + fontFace = generalPlotConfiguration$legendKeysFontFace, + angle = generalPlotConfiguration$legendKeysAngle, + align = generalPlotConfiguration$legendKeysAlign ) legendConfiguration <- tlf::LegendConfiguration$new( position = generalPlotConfiguration$legendPosition, caption = NULL, - title = generalPlotConfiguration$legendTitle, - font = generalPlotConfiguration$legendCaptionFont, + title = legendTitleLabel, # for legend title aesthetics + font = legendKeysFont, # for legend keys aesthetics background = NULL ) @@ -468,7 +582,8 @@ scale = generalPlotConfiguration$xAxisScale, ticks = generalPlotConfiguration$xAxisTicks, ticklabels = generalPlotConfiguration$xAxisTicksLabels, - font = generalPlotConfiguration$xAxisFont + font = generalPlotConfiguration$xAxisFont, + expand = generalPlotConfiguration$xAxisExpand ) # yAxis objects ----------------------------------- @@ -487,7 +602,8 @@ scale = generalPlotConfiguration$yAxisScale, ticks = generalPlotConfiguration$yAxisTicks, ticklabels = generalPlotConfiguration$yAxisTicksLabels, - font = generalPlotConfiguration$yAxisFont + font = generalPlotConfiguration$yAxisFont, + expand = generalPlotConfiguration$yAxisExpand ) # lines ------------------------------------------------------- @@ -522,8 +638,9 @@ # errorbars ------------------------------------------------------- errorbarsConfiguration <- tlf::ThemeAestheticSelections$new( - shape = generalPlotConfiguration$errorbarsShape, size = generalPlotConfiguration$errorbarsSize, + # TODO: https://github.com/Open-Systems-Pharmacology/TLF-Library/issues/347 + # capSize = generalPlotConfiguration$errorbarsCapSize, linetype = generalPlotConfiguration$errorbarsLinetype, alpha = generalPlotConfiguration$errorbarsAlpha ) diff --git a/R/utilities-population.R b/R/utilities-population.R index 73b82b96b..ecb969c86 100644 --- a/R/utilities-population.R +++ b/R/utilities-population.R @@ -9,7 +9,7 @@ #' @export loadPopulation <- function(csvPopulationFile) { validateIsString(csvPopulationFile) - csvPopulationFile <- expandPath(csvPopulationFile) + csvPopulationFile <- .expandPath(csvPopulationFile) populationTask <- .getNetTask("PopulationTask") population <- rClr::clrCall(populationTask, "ImportPopulation", csvPopulationFile) Population$new(population) @@ -34,7 +34,7 @@ splitPopulationFile <- function(csvPopulationFile, numberOfCores, outputFolder, validateIsNumeric(numberOfCores) validateIsString(outputFolder) validateIsString(outputFileName) - csvPopulationFile <- expandPath(csvPopulationFile) + csvPopulationFile <- .expandPath(csvPopulationFile) outputFileName <- enc2utf8(outputFileName) populationTask <- .getNetTask("PopulationTask") rClr::clrCall(populationTask, "SplitPopulation", csvPopulationFile, as.integer(numberOfCores), outputFolder, outputFileName) @@ -97,14 +97,14 @@ populationToTibble <- function(population) { exportPopulationToCSV <- function(population, filePath) { validateIsOfType(population, "Population") validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) df <- populationToDataFrame(population) write.csv(df, file = filePath, row.names = FALSE, fileEncoding = "UTF-8") invisible() } #' @inherit exportPopulationToCSV -savePopulationToCSV <- function(population, filePath) { +.savePopulationToCSV <- function(population, filePath) { exportPopulationToCSV(population, filePath) } @@ -129,7 +129,7 @@ loadAgingDataFromCSV <- function(filePath) { } -#' Creates an population using the PKSim Database +#' Creates an population using the PK-Sim Database #' #' @param populationCharacteristics Characteristics of the population to create as an instance of `OriginData` #' that are actually distributed parameters @@ -145,8 +145,8 @@ createPopulation <- function(populationCharacteristics) { populationFactory <- rClr::clrCallStatic("PKSim.R.Api", "GetPopulationFactory") results <- rClr::clrCall(populationFactory, "CreatePopulation", populationCharacteristics$ref) - netPopulation <- getPropertyValue(results, "IndividualValuesCache") - seed <- getPropertyValue(results, "Seed") + netPopulation <- .getPropertyValue(results, "IndividualValuesCache") + seed <- .getPropertyValue(results, "Seed") population <- Population$new(netPopulation) individualCharacteristics <- NULL @@ -161,7 +161,7 @@ createPopulation <- function(populationCharacteristics) { derivedParameters <- list() - # Even though those parameters are derived parameters, we keep them in the population for consistency purpose with the PKSim export. + # Even though those parameters are derived parameters, we keep them in the population for consistency purpose with the PK-Sim export. standardDerivedParametersToKeep <- c(StandardPath$Weight, StandardPath$BMI, StandardPath$BSA) for (derivedParameterPath in individual$derivedParameters$paths) { @@ -234,8 +234,7 @@ createPopulationCharacteristics <- function(species, gestationalAgeUnit = "week(s)", moleculeOntogenies = NULL, seed = NULL) { - - # Assuming that if this function is called directly, PKSim was either initialized already + # Assuming that if this function is called directly, PK-Sim was either initialized already # or should be initialized automatically initPKSim() @@ -273,11 +272,11 @@ createPopulationCharacteristics <- function(species, populationCharacteristics$population <- population populationCharacteristics$numberOfIndividuals <- numberOfIndividuals populationCharacteristics$proportionOfFemales <- proportionOfFemales - populationCharacteristics$age <- createParameterRange(ageMin, ageMax, ageUnit) - populationCharacteristics$weight <- createParameterRange(weightMin, weightMax, weightUnit) - populationCharacteristics$height <- createParameterRange(heightMin, heightMax, heightUnit) - populationCharacteristics$gestationalAge <- createParameterRange(gestationalAgeMin, gestationalAgeMax, gestationalAgeUnit) - populationCharacteristics$BMI <- createParameterRange(BMIMin, BMIMax, BMIUnit) + populationCharacteristics$age <- .createParameterRange(ageMin, ageMax, ageUnit) + populationCharacteristics$weight <- .createParameterRange(weightMin, weightMax, weightUnit) + populationCharacteristics$height <- .createParameterRange(heightMin, heightMax, heightUnit) + populationCharacteristics$gestationalAge <- .createParameterRange(gestationalAgeMin, gestationalAgeMax, gestationalAgeUnit) + populationCharacteristics$BMI <- .createParameterRange(BMIMin, BMIMax, BMIUnit) populationCharacteristics$seed <- seed for (moleculeOntogeny in moleculeOntogenies) { diff --git a/R/utilities-quantity.R b/R/utilities-quantity.R index 3b34c8dfd..8316201b4 100644 --- a/R/utilities-quantity.R +++ b/R/utilities-quantity.R @@ -86,7 +86,7 @@ getQuantity <- function(path, container, stopIfNotFound = TRUE) { #' `values`. If `NULL` (default), values are assumed to be in base units. If #' not `NULL`, must have the same length as `quantities`. #' -setQuantityValues <- function(quantities, values, units = NULL) { +.setQuantityValues <- function(quantities, values, units = NULL) { # Must turn the input into a list so we can iterate through even when only # one parameter is passed quantities <- toList(quantities) @@ -117,14 +117,13 @@ setQuantityValues <- function(quantities, values, units = NULL) { } } -#' Set the values of parameters in the simulation by path +#' Set the values of quantities in the simulation by path #' -#' @param quantityPaths A single or a list of absolute quantity path +#' @param quantityPaths A single or a list of absolute quantity paths #' @param values A numeric value that should be assigned to the quantities or a #' vector of numeric values, if the value of more than one quantity should be #' changed. Must have the same length as 'quantityPaths'. -#' @param simulation Simulation uses to retrieve quantity instances from given -#' paths. +#' @param simulation Simulation containing the quantities #' @param stopIfNotFound Boolean. If `TRUE` (default) and no quantity exists for #' the given path, an error is thrown. If `FALSE`, a warning is shown to the #' user. @@ -160,7 +159,17 @@ setQuantityValuesByPath <- function(quantityPaths, values, simulation, units = N if (dimension == "") { next } - value <- toBaseUnit(quantityOrDimension = dimension, values = value, unit = units[[i]]) + # If the unit is NULL, the value is assumend to be in base unit and no conversion + # in necessary + if (!is.null(units[[i]])) { + mw <- simulation$molWeightFor(path) + value <- toBaseUnit( + quantityOrDimension = dimension, + values = value, + unit = units[[i]], + molWeight = mw + ) + } } rClr::clrCall( @@ -173,13 +182,77 @@ setQuantityValuesByPath <- function(quantityPaths, values, simulation, units = N } } +#' Get the values of quantities in the simulation by path +#' +#' @param quantityPaths A single or a list of absolute quantity paths +#' @param simulation Simulation containing the quantities +#' @param stopIfNotFound Boolean. If `TRUE` (default) and no quantity exists for +#' the given path, an error is thrown. If `FALSE`, a warning is shown to the +#' user. +#' @param units A string or a list of strings defining the units of returned +#' values. If `NULL` (default), values are returned in base units. If not +#' `NULL`, must have the same length as `quantityPaths`. Single entries may be +#' `NULL`. +#' @examples +#' +#' simPath <- system.file("extdata", "simple.pkml", package = "ospsuite") +#' sim <- loadSimulation(simPath) +#' getQuantityValuesByPath( +#' list("Organism|Liver|Volume", "Organism|Liver|A"), +#' sim, list("ml", NULL) +#' ) +#' @export +getQuantityValuesByPath <- function(quantityPaths, simulation, units = NULL, stopIfNotFound = TRUE) { + validateIsString(quantityPaths) + validateIsOfType(simulation, "Simulation") + + if (!is.null(units)) { + validateIsSameLength(quantityPaths, units) + validateIsString(units, nullAllowed = TRUE) + } + + task <- .getContainerTask() + outputValues <- vector("numeric", length(quantityPaths)) + for (i in seq_along(quantityPaths)) { + path <- enc2utf8(quantityPaths[[i]]) + value <- rClr::clrCall(task, "GetValueByPath", simulation$ref, path, stopIfNotFound) + if (!is.null(units)) { + dimension <- rClr::clrCall( + task, "DimensionNameByPath", + simulation$ref, + path, + stopIfNotFound + ) + # Dimension ca be be empty if the path was not found + if (dimension == "") { + next + } + # If the unit is NULL, the value is assumend to be in base unit and no conversion + # in necessary + if (!is.null(units[[i]])) { + mw <- simulation$molWeightFor(path) + value <- toUnit( + quantityOrDimension = dimension, + values = value, + targetUnit = units[[i]], + molWeight = mw + ) + } + } + + outputValues[[i]] <- value + } + + return(outputValues) +} + #' Scale current values of quantities using a factor #' #' @param quantities A single or a list of `Quantity` #' #' @param factor A numeric value that will be used to scale all quantities #' -scaleQuantityValues <- function(quantities, factor) { +.scaleQuantityValues <- function(quantities, factor) { quantities <- c(quantities) # Test for correct inputs @@ -193,12 +266,12 @@ scaleQuantityValues <- function(quantities, factor) { #' Retrieves the display path of the quantity defined by path in the simulation #' -#' @param paths A single string or array of paths path relative to the `simulation` -#' @param simulation A imulation used to find the entities +#' @param paths A single string or array of paths path relative to the `Simulation` +#' @param simulation A `Simulation` used to find the entities #' #' @return a display path for each entry in paths #' -getQuantityDisplayPaths <- function(paths, simulation) { +.getQuantityDisplayPaths <- function(paths, simulation) { validateIsString(paths) validateIsOfType(simulation, "Simulation") displayResolver <- .getNetTask("FullPathDisplayResolver") @@ -237,3 +310,30 @@ getAllObserverPathsIn <- function(container) { y = c(getAllParameterPathsIn(container), getAllMoleculePathsIn(container)) )) } + +#' Is the value defined by an explicit formula +#' +#' @param path Path to the quantity +#' @param simulation A `Simulation` object that contains the quantity +#' @param stopIfNotFound Boolean. If `TRUE` (default) and no quantity exists +#' for the given path, an error is thrown. If `FALSE`, `FALSE` is returned. +#' +#' @return `TRUE` if the value is an explicit formula, `FALSE` otherwise. +#' Also returns `FALSE` if no quantity with the given path is found and +#' `stopInfNotFound` is set to `FALSE`. +#' @export +#' +#' @examples +#' simPath <- system.file("extdata", "simple.pkml", package = "ospsuite") +#' sim <- loadSimulation(simPath) +#' isExplicitFormulaByPath("Organism|Liver|Volume", sim) # FALSE +isExplicitFormulaByPath <- function(path, simulation, stopIfNotFound = TRUE) { + validateIsString(path, nullAllowed = FALSE) + validateIsOfType(simulation, "Simulation") + + task <- .getContainerTask() + # Check if the quantity is defined by an explicit formula + isFormulaExplicit <- rClr::clrCall(task, "IsExplicitFormulaByPath", simulation$ref, enc2utf8(path), stopIfNotFound) + + return(isFormulaExplicit) +} diff --git a/R/utilities-sensitivity-analysis.R b/R/utilities-sensitivity-analysis.R index dea86d77a..1d0432c97 100644 --- a/R/utilities-sensitivity-analysis.R +++ b/R/utilities-sensitivity-analysis.R @@ -49,14 +49,14 @@ runSensitivityAnalysis <- function(sensitivityAnalysis, sensitivityAnalysisRunOp exportSensitivityAnalysisResultsToCSV <- function(results, filePath) { validateIsOfType(results, "SensitivityAnalysisResults") validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) sensitivityAnalysisTask <- .getNetTask("SensitivityAnalysisTask") rClr::clrCall(sensitivityAnalysisTask, "ExportResultsToCSV", results$ref, results$simulation$ref, filePath) invisible() } #' @inherit exportSensitivityAnalysisResultsToCSV -saveSensitivityAnalysisResultsToCSV <- function(results, filePath) { +.saveSensitivityAnalysisResultsToCSV <- function(results, filePath) { exportSensitivityAnalysisResultsToCSV(results, filePath) } @@ -81,7 +81,7 @@ saveSensitivityAnalysisResultsToCSV <- function(results, filePath) { importSensitivityAnalysisResultsFromCSV <- function(simulation, filePaths) { validateIsOfType(simulation, "Simulation") validateIsString(filePaths) - filePaths <- unlist(lapply(filePaths, function(filePath) expandPath(filePath))) + filePaths <- unlist(lapply(filePaths, function(filePath) .expandPath(filePath))) sensitivityAnalysisTask <- .getNetTask("SensitivityAnalysisTask") results <- rClr::clrCall(sensitivityAnalysisTask, "ImportResultsFromCSV", simulation$ref, filePaths) diff --git a/R/utilities-simulation-results.R b/R/utilities-simulation-results.R index 654d09712..766e3dd73 100644 --- a/R/utilities-simulation-results.R +++ b/R/utilities-simulation-results.R @@ -155,14 +155,14 @@ getOutputValues <- function(simulationResults, exportResultsToCSV <- function(results, filePath) { validateIsOfType(results, "SimulationResults") validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) simulationResultsTask <- .getNetTask("SimulationResultsTask") rClr::clrCall(simulationResultsTask, "ExportResultsToCSV", results$ref, results$simulation$ref, filePath) invisible() } #' @inherit exportResultsToCSV -saveResultsToCSV <- function(results, filePath) { +.saveResultsToCSV <- function(results, filePath) { exportResultsToCSV(results, filePath) } @@ -187,7 +187,7 @@ importResultsFromCSV <- function(simulation, filePaths) { validateIsOfType(simulation, "Simulation") validateIsString(filePaths) simulationResultsTask <- .getNetTask("SimulationResultsTask") - filePaths <- unlist(lapply(filePaths, function(filePath) expandPath(filePath)), use.names = FALSE) + filePaths <- unlist(lapply(filePaths, function(filePath) .expandPath(filePath)), use.names = FALSE) results <- rClr::clrCall(simulationResultsTask, "ImportResultsFromCSV", simulation$ref, filePaths) SimulationResults$new(results, simulation) diff --git a/R/utilities-simulation.R b/R/utilities-simulation.R index fbbc2c216..354ca02f3 100644 --- a/R/utilities-simulation.R +++ b/R/utilities-simulation.R @@ -55,7 +55,7 @@ loadSimulation <- function(filePath, loadFromCache = FALSE, addToCache = TRUE, r simulationPersister <- .getNetTask("SimulationPersister") # Note: We do not expand the variable filePath here as we want the cache to be created using the path given by the user - netSim <- rClr::clrCall(simulationPersister, "LoadSimulation", expandPath(filePath), resetIds) + netSim <- rClr::clrCall(simulationPersister, "LoadSimulation", .expandPath(filePath), resetIds) simulation <- Simulation$new(netSim, filePath) @@ -76,14 +76,19 @@ loadSimulation <- function(filePath, loadFromCache = FALSE, addToCache = TRUE, r saveSimulation <- function(simulation, filePath) { validateIsOfType(simulation, "Simulation") validateIsString(filePath) - filePath <- expandPath(filePath) + filePath <- .expandPath(filePath) simulationPersister <- .getNetTask("SimulationPersister") rClr::clrCall(simulationPersister, "SaveSimulation", simulation$ref, filePath) invisible() } -#' @title -#' Runs one simulation (individual or population) and returns a `SimulationResults` object containing all results of the simulation. + +#' @title Run a single simulation +#' +#' @details +#' +#' Runs one simulation (individual or population) and returns a +#' `SimulationResults` object containing all results of the simulation. #' #' @param simulation One `Simulation` to simulate. #' @param population Optional instance of a `Population` to use for the simulation. This is only used when simulating one simulation @@ -124,7 +129,7 @@ runSimulation <- function(simulation, population = NULL, agingData = NULL, simul #' @title Runs multiple simulations concurrently. #' #' @details For multiple simulations, only individual simulations are possible. -#' For single simulatio, either individual or population simulations can be +#' For single simulation, either individual or population simulations can be #' performed. #' #' @param simulations One `Simulation` or list of `Simulation` objects @@ -333,7 +338,10 @@ createSimulationBatch <- function(simulation, parametersOrPaths = NULL, molecule #' @param silentMode If `TRUE`, no warnings are displayed if a simulation fails. #' Default is `FALSE`. #' -#' @return Nested list of `SimulationResults` objects. The first level of the list are the IDs of the simulations of SimulationBatches, containing a list of `SimulationResults` for each set of parameter/initial values. If a simulation with a parameter/initial values set fails, the result for this run is `NULL` +#' @return Nested list of `SimulationResults` objects. The first level of the +#' fist are the IDs of the SimulationBatches, containing a list of +#' `SimulationResults` for each set of parameter/initial values. If a simulation +#' with a parameter/initial values set fails, the result for this run is `NULL` #' @export #' #' @examples @@ -369,35 +377,41 @@ runSimulationBatches <- function(simulationBatches, simulationRunOptions = NULL, rClr::clrSet(simulationRunner, "SimulationRunOptions", simulationRunOptions$ref) simulationBatches <- c(simulationBatches) - # Result Id <-> simulation batch pointer id map to get the correct simulation for the results. - # Using the Id of the pointer instead of the Id of the simulation as multiple + # Result Id <-> simulation batch id map to get the correct simulation for the results. + # Using the Id of the batch instead of the Id of the simulation as multiple # SimulationBatches can be created with the same simulation # Each SimulationBatchRunValues has its own id, which will be the id of the result - resultsIdSimulationIdMap <- list() + resultsIdSimulationBatchIdMap <- list() # Map of simulations ids to simulations objects - simulationIdSimulationMap <- vector("list", length(simulationBatches)) + simulationBatchIdSimulationMap <- vector("list", length(simulationBatches)) # Iterate through all simulation batches for (simBatchIndex in seq_along(simulationBatches)) { simBatch <- simulationBatches[[simBatchIndex]] - simBatchId <- rClr::clrGet(simBatch$ref, "Id") - simulationIdSimulationMap[[simBatchIndex]] <- simBatch$simulation - names(simulationIdSimulationMap)[[simBatchIndex]] <- simBatchId + simBatchId <- simBatch$id + simulationBatchIdSimulationMap[[simBatchIndex]] <- simBatch$simulation + names(simulationBatchIdSimulationMap)[[simBatchIndex]] <- simBatchId # Ids of the values of the batch valuesIds <- simBatch$runValuesIds # All results of this batch have the id of the same simulation - resultsIdSimulationIdMap[valuesIds] <- simBatchId + resultsIdSimulationBatchIdMap[valuesIds] <- simBatchId # Add the batch to concurrent runner rClr::clrCall(simulationRunner, "AddSimulationBatch", simBatch$ref) } # Run the batch with the ConcurrentSimulationRunner results <- rClr::clrCall(simulationRunner, "RunConcurrently") - simulationResults <- .getConcurrentSimulationRunnerResults(results = results, resultsIdSimulationIdMap = resultsIdSimulationIdMap, simulationIdSimulationMap = simulationIdSimulationMap, silentMode = silentMode) + simulationResults <- .getConcurrentSimulationRunnerResults( + results = results, + resultsIdSimulationIdMap = resultsIdSimulationBatchIdMap, + simulationIdSimulationMap = simulationBatchIdSimulationMap, + silentMode = silentMode + ) - # output: list of lists of SimulationResults, one list per SimulationBatch - output <- lapply(names(simulationIdSimulationMap), function(simId) { - simulationResults[which(resultsIdSimulationIdMap == simId)] + # Returned is a named list of results with names being the IDs of the batches + output <- lapply(names(simulationBatchIdSimulationMap), function(simBatchId) { + simulationResults[which(resultsIdSimulationBatchIdMap == simBatchId)] }) + names(output) <- names(simulationBatchIdSimulationMap) # Dispose of the runner to release any possible instances still in memory (.NET side) rClr::clrCall(simulationRunner, "Dispose") @@ -516,6 +530,19 @@ getAllStateVariablesPaths <- function(simulation) { return(allQantitiesPaths) } +#' Get the paths of all state variable parameters of the simulation +#' +#' @param simulation `Simulation` object +#' @details List of paths of all state variable parameters. +#' +#' @return A list of paths +#' @export +getAllStateVariableParametersPaths <- function(simulation) { + validateIsOfType(simulation, type = "Simulation") + allStateVariableParamsPaths <- .getAllEntityPathsIn(container = simulation, entityType = Parameter, method = "AllStateVariableParameterPathsIn") + return(allStateVariableParamsPaths) +} + #' Export simulation PKMLs for given `individualIds`. Each pkml file will contain the original simulation updated with parameters of the corresponding individual. #' #' @param population A population object typically loaded with `loadPopulation` @@ -540,7 +567,7 @@ exportIndividualSimulations <- function(population, individualIds, outputFolder, validateIsOfType(simulation, "Simulation") validateIsOfType(population, "Population") individualIds <- c(individualIds) - outputFolder <- expandPath(outputFolder) + outputFolder <- .expandPath(outputFolder) simuationPaths <- NULL for (individualId in individualIds) { @@ -590,3 +617,125 @@ exportIndividualSimulations <- function(population, individualIds, outputFolder, } return(simulationResults) } + + +#' @keywords internal +#' @noRd +.addBranch <- function(originalPathString, arrayToGo) { + # Function to create a multilayered list called endList with a branched + # structure corresponding to the structure of arrayToGo that terminates with a + # string called 'path' that is equal to the string originalString + if (length(arrayToGo) == 0) { + # If arrayToGo is empty, create a terminal list with a string called 'path' + # and value equal to originalString + endList <- list() + endList$path <- originalPathString + return(endList) + } else { + # If arrayToGo is still not empty, remove its leading element and create a + # sub-branch list corresponding to the structure of the remaining elements + # of arrayToGo + newBranch <- list() + newBranch[[arrayToGo[1]]] <- .addBranch(originalPathString, tail(arrayToGo, -1)) + + return(newBranch) + } +} + +#' @keywords internal +#' @noRd +.nextStep <- function(listSoFar, originalString, arrayToGo) { + # Recursive function that adds a multilayer list to listSoFar that has a + # branched structure representing the vector of strings arrayToGo. + if (length(arrayToGo) == 0) { + # If end of string vector arrayToGo has been reached, create a vector called + # 'path' and give it the value 'originalString'. + listSoFar$path <- originalString + } else { + # End of branch has not been reached. If this portion of the string vector + # arrayToGo has not been added to listToGo yet, add it using the function + # .addBranch + if (is.null(listSoFar[[arrayToGo[1]]])) { + listSoFar[[arrayToGo[1]]] <- .addBranch(originalString, tail(arrayToGo, -1)) + } + # If this portion of the string vector arrayToGo has already been added to + # listSoFar, remove the leading element of arrayToGo and recursively apply + # this function using the remaining elements of arrayToGo. + else { + listSoFar[[arrayToGo[1]]] <- .nextStep(listSoFar[[arrayToGo[1]]], originalString, tail(arrayToGo, -1)) + } + } + + return(listSoFar) +} + + +#' Get simulation tree +#' +#' @description +#' +#' Given a simulation file path or an instance of a simulation, traverses the +#' simulation structure and returns a tree like structure allowing for intuitive +#' navigation in the simulation tree. +# +#' @param simulationOrFilePath Full path of the simulation to load or instance +#' of a simulation. +#' @param quantityType A vector of strings that specify the types of the +#' entities to be included in the tree. The types can be any combination of +#' "Quantity", "Molecule", "Parameter" and "Observer". +#' +#' @return +#' +#' A list with a branched structure representing the path tree of entities in +#' the simulation file that fall under the types specified in `quantityType`. At +#' the end of each branch is a string called 'path' that is the path of the +#' quantity represented by the branch. +#' +#' @importFrom utils tail +#' @examples +#' simPath <- system.file("extdata", "simple.pkml", package = "ospsuite") +#' sim <- loadSimulation(simPath) +#' +#' tree <- getSimulationTree(sim) +#' +#' liver_volume_path <- tree$Organism$Liver$Volume$path +#' @export +getSimulationTree <- function(simulationOrFilePath, quantityType = "Quantity") { + validateIsOfType(simulationOrFilePath, c("Simulation", "character")) + + quantityTypeList <- list( + "Quantity" = getAllQuantityPathsIn, + "Molecule" = getAllMoleculePathsIn, + "Parameter" = getAllParameterPathsIn, + "Observer" = getAllObserverPathsIn + ) + + validateIsIncluded(values = quantityType, parentValues = names(quantityTypeList)) + + simulation <- simulationOrFilePath + if (isOfType(simulationOrFilePath, "character")) { + simulation <- loadSimulation(simulationOrFilePath) + } + + # Build a vector, with no duplicated entries, of all paths corresponding to + # entities in `simulation` that fall under the types specified in quantityType + allPaths <- sapply(quantityType, function(type) { + quantityTypeList[[type]](simulation) + }) %>% + unname() %>% + unlist() %>% + unique() + + # Initiate list to be returned as a null list. + pathEnumList <- list() + + for (path in allPaths) { + # Convert the path string to a vector of strings, each representing a branch portion. + pathArray <- toPathArray(path) + + # Begin recursive loop to generate branched list. + pathEnumList <- .nextStep(pathEnumList, path, pathArray) + } + + return(pathEnumList) +} diff --git a/R/utilities-units.R b/R/utilities-units.R index 953912ff0..b9bca8fb8 100644 --- a/R/utilities-units.R +++ b/R/utilities-units.R @@ -5,7 +5,7 @@ #' @export hasDimension <- function(dimension) { validateIsString(dimension) - dimensionTask <- getDimensionTask() + dimensionTask <- .getDimensionTask() rClr::clrCall(dimensionTask, "HasDimension", enc2utf8(dimension)) } @@ -30,7 +30,7 @@ validateDimension <- function(dimension) { hasUnit <- function(unit, dimension) { validateIsString(unit) validateDimension(dimension) - dimensionTask <- getDimensionTask() + dimensionTask <- .getDimensionTask() rClr::clrCall(dimensionTask, "HasUnit", enc2utf8(dimension), .encodeUnit(unit)) } @@ -54,7 +54,7 @@ validateUnit <- function(unit, dimension) { #' @return #' If validations are successful, `NULL` is returned. Otherwise, error is #' signaled. -validateHasUnit <- function(quantity, unit) { +.validateHasUnit <- function(quantity, unit) { validateIsOfType(quantity, "Quantity") validateIsString(unit) if (quantity$hasUnit(unit)) { @@ -71,7 +71,7 @@ validateHasUnit <- function(quantity, unit) { #' @export getBaseUnit <- function(dimension) { validateDimension(dimension) - dimensionTask <- getDimensionTask() + dimensionTask <- .getDimensionTask() rClr::clrCall(dimensionTask, "BaseUnitFor", enc2utf8(dimension)) } @@ -267,7 +267,7 @@ allAvailableDimensions <- function() { getDimensionForUnit <- function(unit) { validateIsString(unit) unit <- .encodeUnit(unit) - dimensionTask <- getDimensionTask() + dimensionTask <- .getDimensionTask() dim <- rClr::clrCall(dimensionTask, "DimensionForUnit", unit) ifNotNull(dim, rClr::clrGet(dim, "Name")) } @@ -286,7 +286,7 @@ getDimensionForUnit <- function(unit) { #' @export getUnitsForDimension <- function(dimension) { validateIsString(dimension) - dimensionTask <- getDimensionTask() + dimensionTask <- .getDimensionTask() rClr::clrCall(dimensionTask, "AllAvailableUnitNamesFor", enc2utf8(dimension)) } @@ -294,7 +294,7 @@ getUnitsForDimension <- function(dimension) { #' This is purely for optimization purposes #' #' @return An instance of the Task -getDimensionTask <- function() { +.getDimensionTask <- function() { dimensionTask <- ospsuiteEnv$dimensionTask if (is.null(dimensionTask)) { dimensionTask <- .getNetTask("DimensionTask") @@ -318,7 +318,7 @@ getDimensionTask <- function() { #' @export getDimensionByName <- function(name) { validateIsString(name) - dimensionTask <- getDimensionTask() + dimensionTask <- .getDimensionTask() rClr::clrCall(dimensionTask, "DimensionByName", enc2utf8(name)) } @@ -334,9 +334,9 @@ getDimensionByName <- function(name) { #' #' @examples #' -#' ospsuite:::getUnitsEnum() +#' ospsuite:::.getUnitsEnum() #' @keywords internal -getUnitsEnum <- function() { +.getUnitsEnum <- function() { dimensions <- allAvailableDimensions() errors <- c() units <- lapply(dimensions, function(dimension) { @@ -373,9 +373,9 @@ getUnitsEnum <- function() { #' #' @examples #' -#' ospsuite:::getDimensionsEnum() +#' ospsuite:::.getDimensionsEnum() #' @keywords internal -getDimensionsEnum <- function() { +.getDimensionsEnum <- function() { enum(allAvailableDimensions()) } @@ -393,40 +393,17 @@ ospDimensions <- list() #' @export ospUnits <- list() -initializeDimensionAndUnitLists <- function() { +.initializeDimensionAndUnitLists <- function() { # This initializes the two lists in the parent environment which is the package environments - ospDimensions <<- getDimensionsEnum() - ospUnits <<- getUnitsEnum() + ospDimensions <<- .getDimensionsEnum() + ospUnits <<- .getUnitsEnum() } -#' Convert data frame to common units -#' -#' @description -#' -#' When multiple (observed and/or simulated) datasets are present in a data -#' frame, they are likely to have different units. This function helps to -#' convert them to a common unit specified by the user. -#' -#' This is especially helpful while plotting since the quantities from different -#' datasets to be plotted on the X-and Y-axis need to have same units to be -#' meaningfully compared. -#' -#' @note -#' -#' Molecular weight is **required** for the conversion between certain -#' dimensions (`Amount`, `Mass`, `Concentration (molar)`, and `Concentration -#' (mass)`). Therefore, if molecular weight is missing for these dimension, the -#' unit conversion will fail. -#' -#' @return A data frame with measurement columns transformed to have common units. +#' Convert a data frame to common units #' #' @param data A data frame (or a tibble) from `DataCombined$toDataFrame()`. -#' @param xUnit,yUnit Target units for `xValues` and `yValues`, respectively. If -#' not specified (`NULL`), first of the existing units in the respective -#' columns (`xUnit` and `yUnit`) will be selected as the common unit. For -#' available dimensions and units, see `ospsuite::ospDimensions` and -#' `ospsuite::ospUnits`, respectively. +#' @inheritParams convertUnits #' #' @seealso toUnit #' @@ -455,10 +432,21 @@ initializeDimensionAndUnitLists <- function() { #' ospsuite:::.unitConverter(df, xUnit = ospUnits$Time$s, yUnit = ospUnits$Amount$mmol) #' @keywords internal .unitConverter <- function(data, xUnit = NULL, yUnit = NULL) { - # No validation of inputs for this non-exported function. # All validation will take place in the `DataCombined` class itself. + # early return -------------------------- + + # Return early if there are only unique units present in the provided data and + # `xUnit` and `yUnit` arguments are `NULL`. This helps avoid expensive and + # redundant computations. + # + # *DO NOT* use short-circuiting `&&` logical operator here. + if (length(unique(data$xUnit)) == 1L & is.null(xUnit) & + length(unique(data$yUnit)) == 1L & is.null(yUnit)) { + return(data) + } + # target units -------------------------- # The observed and simulated data should have the same units for @@ -499,8 +487,8 @@ initializeDimensionAndUnitLists <- function() { # # If there is no `yErrorValues` column in the entered data frame, it doesn't # make sense for this function to introduce a new column called `yErrorUnit`. - if (("yErrorValues" %in% names(data)) && - !("yErrorUnit" %in% names(data))) { + if (("yErrorValues" %in% colnames(data)) && + !("yErrorUnit" %in% colnames(data))) { data <- dplyr::mutate(data, yErrorUnit = yUnit) } @@ -558,13 +546,20 @@ initializeDimensionAndUnitLists <- function() { ) # yUnit error - if ("yErrorValues" %in% names(data)) { + if ("yErrorValues" %in% colnames(data)) { yErrorDataList <- .removeEmptyDataFrame(split(data, list(data$yErrorUnitSplit, data$molWeightSplit))) data <- purrr::map_dfr( .x = yErrorDataList, .f = function(data) .yErrorUnitConverter(data, yTargetUnit) ) + } else { + # For some reason, if the user dataset doesn't have error values, but + # still have columns about error units, update them as well. The quantity + # and its error should always have the same unit in the final data frame. + if ("yErrorUnit" %in% colnames(data)) { + data <- dplyr::mutate(data, yErrorUnit = yUnit) + } } # clean up and return -------------------------- @@ -625,6 +620,17 @@ initializeDimensionAndUnitLists <- function() { molWeightUnit = ospUnits$`Molecular weight`$`g/mol` ) + if ("lloq" %in% colnames(yData)) { + yData$lloq <- toUnit( + quantityOrDimension = yData$yDimension[[1]], + values = yData$lloq, + targetUnit = yTargetUnit, + sourceUnit = yData$yUnit[[1]], + molWeight = yData$molWeight[[1]], + molWeightUnit = ospUnits$`Molecular weight`$`g/mol` + ) + } + yData$yUnit <- yTargetUnit return(yData) @@ -633,6 +639,14 @@ initializeDimensionAndUnitLists <- function() { #' @keywords internal #' @noRd .yErrorUnitConverter <- function(yData, yTargetUnit) { + # If error type is geometric, conversion of `yValues` to different units + # should not trigger conversion of error values (and units) + if ("yErrorType" %in% colnames(yData) && + !is.na(unique(yData$yErrorType)) && + unique(yData$yErrorType) == DataErrorType$GeometricStdDev) { + return(yData) + } + yData$yErrorValues <- toUnit( quantityOrDimension = yData$yDimension[[1]], values = yData$yErrorValues, @@ -686,14 +700,15 @@ initializeDimensionAndUnitLists <- function() { mostFrequentUnit <- unitUsageFrequency %>% # Select only the row(s) with maximum frequency. - dplyr::filter(unitFrequency == max(unitFrequency)) %>% - # In case of ties, there will be more than one row. In such cases, the first - # unit is selected. + # + # In case of ties, there can be more than one row. In such cases, setting + # `with_ties = FALSE` make sure that only the first row (and the + # corresponding) unit will be selected. # # Do *not* select randomly as that would introduce randomness in plotting # functions with each run of the plotting function defaulting to a different # unit. - dplyr::slice_head(n = 1L) %>% + dplyr::slice_max(unitFrequency, n = 1L, with_ties = FALSE) %>% # Remove the frequency column, which is not useful outside the context of # this function. dplyr::select(-unitFrequency) diff --git a/R/zzz.R b/R/zzz.R index 80bfa9cd6..35f537b47 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,6 +1,5 @@ # nocov start .onLoad <- function(...) { - # Only for x64 bits packages. # This is required to avoid error when package is being checked on CI for x86 is64 <- (.Machine$sizeof.pointer == 8) @@ -8,6 +7,12 @@ return() } - initPackage() + # Now verify that the package is running on R 64 + isR64 <- R.version$arch == "x86_64" + if (!isR64) { + stop("64 bit version of R is required.") + } + + .initPackage() } # nocov end diff --git a/README.md b/README.md index 5c1fd7aec..7cdbd85f8 100644 --- a/README.md +++ b/README.md @@ -1,136 +1,207 @@ -# OSPSuite-R - - - -[![AppVeyor build status](https://ci.appveyor.com/api/projects/status/github/Open-Systems-Pharmacology/OSPSuite-R?branch=develop&svg=true)](https://ci.appveyor.com/project/open-systems-pharmacology-ci/ospsuite-r) -[![codecov](https://codecov.io/gh/Open-Systems-Pharmacology/OSPSuite-R/branch/develop/graph/badge.svg)](https://codecov.io/gh/Open-Systems-Pharmacology/OSPSuite-R) - - - -# Overview - -The **ospsuite-R** package provides the functionality of loading, manipulating, and simulating the simulations created in the Open Systems Pharmacology Software tools PK-Sim and MoBi. - -- [Documentation](#documentation) -- [Installation](#installation) -- [Usage](#usage) -- [Known issues](#known-issues) -- [Code of conduct](#code-of-conduct) -- [Contribution](#contribution) -- [Licence](#licence) - -# Documentation - -Please refer to the [online documentation](https://www.open-systems-pharmacology.org/OSPSuite-R/) for more details on the package - -# Installation - -The **ospsuite-R** package is compatible with version 3.6.x **AND** version 4.x.x of R. One of its dependency, **rClr** needs to be installed specifically for the targeted R version. Please follow the installation instructions below. - -**ospsuite** requires following packages to be installed: - -From CRAN: -- [R6](https://github.com/r-lib/R6) -- [stringr](https://cran.r-project.org/web/packages/stringr/) -- [readr](https://cran.r-project.org/web/packages/readr/index.html) - -Must be downloaded manually: -- rClr - - [For R 4.x.x](https://github.com/Open-Systems-Pharmacology/rClr/releases/download/v0.9.1/rClr_0.9.1.zip) - - [For R 3.6.x](https://github.com/Open-Systems-Pharmacology/rClr/releases/download/v0.9.1-R3/rClr_0.9.1.zip) - - - -## Under Windows - -The release version of the package comes as a binary *.zip and can be downloaded from [here](https://github.com/Open-Systems-Pharmacology/OSPSuite-R/releases). - -The package also requires the Visual C++ Runtime that is installed with OSPS and can be manually downloaded [here](https://aka.ms/vs/16/release/vc_redist.x64.exe). - -```r -# Install dependencies -install.packages('R6') - -# Install rClr from local file -install.packages(pathTorCLR.zip, repos = NULL) - -# Install ospsuite-r from local file -install.packages(pathToOSPSuite.zip, repos = NULL) -``` - -## Under Linux - -The **ospsuite** package has been tested under Linux distributions CentOS 7 and Ubuntu 18. Some functionality, such as creating individuals, is not available under Linux. Installation under Linux requires several prerequisites, the detailed instructions can be found in the [Wiki](https://github.com/Open-Systems-Pharmacology/OSPSuite-R/wiki/Setup-ospsuite-R-on-Ubuntu). -For other Linux distributions Docker containers can be used (Dockerfiles based on CentOS 7 and Ubuntu 18 are available under https://github.com/Open-Systems-Pharmacology/OSPSuite-R/releases ) - -## Build from source - -You can clone the GIT repository and build the package from source. - -### How to update dependencies from nuget? - -- `git submodule update --init --recursive` to install all submodules -- Make sure you have [ruby](https://www.ruby-lang.org/de/downloads/) install and that it is available in your path -- Run `rake postclean` or simply double click on `postclean.bat`. This will update all nuget packages and copy the dependencies in the package `inst/lib` folder. - -# Usage - -In general, every workflow starts with loading a simulation that has been exported to the `*.pkml` format. The method `loadSimulation()` returns the corresponding simulation that is used as input of other methods. The user can then change values of parameters and initial conditions, run the simulation, and retrieve the simulated results. - -```r -library(ospsuite) - -# Load a simulation -simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") -sim <- loadSimulation(simFilePath) - -# Get the parameter "Dose" -doseParamPath <- "Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose" -doseParam <- getParameter(doseParamPath, sim) - -# Change the dose to 350mg. The values has to be converted to base unit, first -newValue <- toBaseUnit(quantity = doseParam, values = 350, unit = "mg") -setParameterValues(parameters = doseParam, values = newValue) - -# Simulate -simResults <- runSimulation(simulation = sim) -# Retrieve the results -simulatedValues <- getOutputValues(simulationResults = simResults) - -# Plot time-concentration profile -plot(simulatedValues$data$Time, simulatedValues$data$`Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)`, -type = "l", -xlab = "Time [min]", -ylab = "Concentration [µmol/l]") -``` - -More detailed description of the methods and the typical workflows can be found in the vignettes. You can see the list of all vignettes available for **ospsuite** by calling - -```r -vignette(package = "ospsuite") -``` - -To open a specific vignette, call - -```r -# Insert the name of the vignette you want to view as the argument -vignette("introduction-ospsuite") -``` - -# Known issues - -**RStudio crashes when trying to load a workspace.** The ospsuite package uses the features implemented in PK-Sim and MoBi by creating .NET objects (e.g. a simulation) and using them from R. These objects cannot be saved as part of the workspace and reloaded on next start. When trying to do so, RStudio simply crashes. There is no possibility to overcome this limitation. To prevent RStudio from crashing, make sure to disable the check-box "Restore .RData into workspace at startup" in the options of RStudio. Keep in mind that you can also change this setting for specific projects. - -# Code of conduct - -Everyone interacting in the Open Systems Pharmacology community (codebases, issue trackers, chat rooms, mailing lists etc...) is expected to follow the Open Systems Pharmacology [code of conduct](https://github.com/Open-Systems-Pharmacology/Suite/blob/master/CODE_OF_CONDUCT.md). - -# Contribution - -We encourage contribution to the Open Systems Pharmacology community. Before getting started please read the [contribution guidelines](https://github.com/Open-Systems-Pharmacology/Suite/blob/master/CONTRIBUTING.md). If you are contributing code, please be familiar with the [coding standards](https://github.com/Open-Systems-Pharmacology/Suite/blob/master/CODING_STANDARDS_R.md). - -# License - -OSPSuite-R is released under the [GPLv2 License](LICENSE). - -All trademarks within this document belong to their legitimate owners. +# OSPSuite-R + + + + +AppVeyor build status + +codecov + + + +# Overview + +The **ospsuite-R** package provides the functionality of loading, manipulating, and simulating the simulations created in the Open Systems Pharmacology Software tools PK-Sim and MoBi. + +- [Documentation](#documentation) +- [Installation](#installation) +- [Usage](#usage) +- [Known issues](#known-issues) +- [Code of conduct](#code-of-conduct) +- [Contribution](#contribution) +- [Licence](#licence) + +# Documentation + +If you are reading this on GitHub README, please refer to the [online documentation](https://www.open-systems-pharmacology.org/OSPSuite-R/) for more details on the package. + +In particular, we would recommend that you read the articles in the following order: + +* [Get Started](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/ospsuite.html) +* [Loading a simulation and accessing entities](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/load-get.html) +* [Changing parameter and molecule start values](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/set-values.html) +* [Running a simulation](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/run-simulation.html) +* [Efficient calculations](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/efficient-calculations.html) +* [Creating individuals](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/create-individual.html) +* [Population simulations](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/create-run-population.html) +* [PK Analysis](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/pk-analysis.html) +* [Sensitivity analysis](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/sensitivity-analysis.html) +* [Table parameters](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/table-parameters.html) +* [Dimensions and Units](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/unit-conversion.html) +* [Working with data sets and import from excel](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/observed-data.html) +* [Working with `DataCombined` class](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/data-combined.html) +* [Visualizations with `DataCombined`](https://www.open-systems-pharmacology.org/OSPSuite-R/articles/data-combined-plotting.html) + +# Installation + +The **ospsuite-R** package is compatible with version 3.6.x **AND** version 4.x.x of R. One of its dependency, **rClr** needs to be installed specifically for the targeted R version. Please follow the installation instructions below. + +**ospsuite** requires following packages to be installed: + +From CRAN: + +- [dplyr](https://cran.r-project.org/web/packages/dplyr/index.html) +- [purrr](https://cran.r-project.org/web/packages/purrr/index.html) +- [R6](https://cran.r-project.org/web/packages/R6/index.html) +- [readr](https://cran.r-project.org/web/packages/readr/index.html) +- [stringr](https://cran.r-project.org/web/packages/stringr/index.html) +- [tidyr](https://cran.r-project.org/web/packages/tidyr/index.html) +- [ggplot2](https://cran.r-project.org/web/packages/ggplot2/index.html) +- [rlang](https://cran.r-project.org/web/packages/rlang/index.html) +- [jsonlite](https://cran.r-project.org/web/packages/jsonlite/index.html) +- [patchwork](https://cran.r-project.org/web/packages/patchwork/index.html) +- [cowplot](https://cran.r-project.org/web/packages/cowplot/index.html) +- [scales](https://cran.r-project.org/web/packages/scales/index.html) + + +Must be downloaded manually: + +- rClr + - [For R 4.x.x](https://github.com/Open-Systems-Pharmacology/rClr/releases/download/v0.9.2/rClr_0.9.2.zip) + - [For R 3.6.x](https://github.com/Open-Systems-Pharmacology/rClr/releases/download/v0.9.1-R3/rClr_0.9.1.zip) + +- [ospsuite.utils](https://github.com/Open-Systems-Pharmacology/OSPSuite.RUtils/releases/latest) + +- [tlf](https://github.com/Open-Systems-Pharmacology/TLF-Library/releases/latest) + + +## Under Windows + +The release version of the package comes as a binary `*.zip` and can be downloaded from [here](https://github.com/Open-Systems-Pharmacology/OSPSuite-R/releases). + +If you use [RStudio IDE](https://www.rstudio.com/), you can use the *Install* option in the *Packages* pane and select the option *Install from -> Package Archive File* to install a package from binary `*.zip` files. + +To install manually, follow these instructions: + +```r +# Install dependencies (e.g. R6) which are on CRAN +install.packages('R6') + +# Install `{rClr}` from local file +# (`pathTo_rCLR.zip` here should be replaced with the actual path to the `.zip` file) +install.packages(pathTo_rCLR.zip, repos = NULL) + +# Install `{ospsuite.utils}` from local file +# (`pathTo_ospsuite.utils.zip` here should be replaced with the actual path to the `.zip` file) +install.packages(pathTo_ospsuite.utils.zip, repos = NULL) + +# Install `{tlf}` from local file +# (`pathTo_tlf.zip` here should be replaced with the actual path to the `.zip` file) +install.packages(pathTo_tlf.zip, repos = NULL) + +# Install `{ospsuite}` from local file +# (`pathToOSPSuite.zip` here should be replaced with the actual path to the `.zip` file) +install.packages(pathToOSPSuite.zip, repos = NULL) +``` + +The package also requires the Visual C++ Runtime that is installed with OSPS and can be manually downloaded [here](https://aka.ms/vs/16/release/vc_redist.x64.exe). + +## Under Linux + +The **ospsuite** package has been tested under Linux distributions CentOS 7 and Ubuntu 18. Some functionality, such as creating individuals, is not available under Linux. Installation under Linux requires several prerequisites, the detailed instructions can be found in the [Wiki](https://github.com/Open-Systems-Pharmacology/OSPSuite-R/wiki/Setup-ospsuite-R-on-Ubuntu). +For other Linux distributions Docker containers can be used (Dockerfiles based on CentOS 7 and Ubuntu 18 are available under https://github.com/Open-Systems-Pharmacology/OSPSuite-R/releases ) + +## Build from source + +You can clone the GIT repository and build the package from source. + +### How to update dependencies from nuget? + +- `git submodule update --init --recursive` to install all submodules +- Make sure you have [ruby](https://www.ruby-lang.org/de/downloads/) install and that it is available in your path +- Run `rake postclean` or simply double click on `postclean.bat`. This will update all nuget packages and copy the dependencies in the package `inst/lib` folder. + +# Usage + +In general, every workflow starts with loading a simulation that has been exported to the `*.pkml` format. The method `loadSimulation()` returns the corresponding simulation that is used as input of other methods. The user can then change values of parameters and initial conditions, run the simulation, and retrieve the simulated results. + +```r +library(ospsuite) + +# Load a simulation +simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite") +sim <- loadSimulation(simFilePath) + +# Get the parameter "Dose" +doseParamPath <- "Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose" +doseParam <- getParameter(doseParamPath, sim) + +# Change the dose to 350mg. The values has to be converted to base unit, first +newValue <- toBaseUnit(quantity = doseParam, values = 350, unit = "mg") +setParameterValues(parameters = doseParam, values = newValue) + +# Simulate +simResults <- runSimulation(simulation = sim) +# Retrieve the results +simulatedValues <- getOutputValues(simulationResults = simResults) + +# Plot time-concentration profile +plot(simulatedValues$data$Time, simulatedValues$data$`Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)`, +type = "l", +xlab = "Time [min]", +ylab = "Concentration [µmol/l]") +``` + +More detailed description of the methods and the typical workflows can be found in the vignettes. You can see the list of all vignettes available for **ospsuite** by calling- + +```r +vignette(package = "ospsuite") +``` + +To open a specific vignette, call- + +```r +# Insert the name of the vignette you want to view as the argument +vignette("introduction-ospsuite") +``` + +# Known issues + + +- Loading `ospsuite` might fail if your systems locale is not set to *English*, e.g.: + +```ibrary(ospsuite) +载入需要的程辑包:rClr +Loading the dynamic library for Microsoft .NET runtime... +Loaded Common Language Runtime version 4.0.30319.42000 + +Error: package or namespace load failed for ‘ospsuite’: +loadNamespace()里算'ospsuite'时.onLoad失败了,详细内容: +调用: rClr::clrCall(dimensionTask, "AllAvailableUnitNamesFor", enc2utf8(dimension)) +错误: Type: System.Collections.Generic.KeyNotFoundException +Message: Dimension 'CV mmHg*s虏/ml' not available in DimensionFactory. +... +``` + +-- On Windows, set `Settings > Language > Administrative language settings > Current language for non-Unicode programs` +to `English (United States)` and reboot. +-- On Linux, set the environment variable `LC_ALL` before starting R: +``` +export LC_ALL=en_US.UTF-8 +``` + +- **RStudio crashes when trying to load a workspace.** The ospsuite package uses the features implemented in PK-Sim and MoBi by creating `.NET` objects (e.g. a simulation) and using them from R. These objects cannot be saved as part of the workspace and reloaded on next start. When trying to do so, RStudio simply crashes. There is no possibility to overcome this limitation. To prevent RStudio from crashing, make sure to disable the check-box "Restore `.RData` into workspace at startup" in the options of RStudio. Keep in mind that you can also change this setting for specific projects. + +# Code of Conduct + +Everyone interacting in the Open Systems Pharmacology community (codebases, issue trackers, chat rooms, mailing lists etc.) is expected to follow the Open Systems Pharmacology [code of conduct](https://github.com/Open-Systems-Pharmacology/Suite/blob/master/CODE_OF_CONDUCT.md). + +# Contribution + +We encourage contribution to the Open Systems Pharmacology community. Before getting started please read the [contribution guidelines](https://github.com/Open-Systems-Pharmacology/Suite/blob/master/CONTRIBUTING.md). If you are contributing code, please be familiar with the [coding standards](https://github.com/Open-Systems-Pharmacology/Suite/blob/master/CODING_STANDARDS_R.md). + +# License + +OSPSuite-R is released under the [GPLv2 License](LICENSE). + +All trademarks within this document belong to their legitimate owners. diff --git a/_pkgdown.yml b/_pkgdown.yml index 4f83dc5f3..34dd983d1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -3,6 +3,9 @@ url: https://www.open-systems-pharmacology.org/OSPSuite-R/ template: bootstrap: 5 +development: + mode: devel + authors: Indrajeet Patil: href: https://sites.google.com/site/indrajeetspatilmorality/ @@ -20,6 +23,13 @@ articles: - sensitivity-analysis - create-individual - create-run-population + - title: Figure creation + navbar: Figure creation + desc: How to create standard figures + contents: + - observed-data + - data-combined + - data-combined-plotting - title: Miscellaneous navbar: Miscellaneous diff --git a/appveyor-3.6.yml b/appveyor-3.6.yml index bb49deedf..342b75fdb 100644 --- a/appveyor-3.6.yml +++ b/appveyor-3.6.yml @@ -1,10 +1,13 @@ image: Visual Studio 2019 -version: '{build}' +# uncomment to use global ospsuite version (and comment line underneath) +# version: '{build}' +version: '11.1.{build}' # Download script file from GitHub init: - - ps: Update-AppveyorBuild -Version "$($env:ospsuite_version).$($env:appveyor_build_version)" +# uncomment to use global ospsuite version +# - ps: Update-AppveyorBuild -Version "$($env:ospsuite_version).$($env:appveyor_build_version)" - ps: | $ErrorActionPreference = "Stop" Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" diff --git a/appveyor-nightly.yml b/appveyor-nightly.yml index 7087fe3cf..49a70fa6d 100644 --- a/appveyor-nightly.yml +++ b/appveyor-nightly.yml @@ -1,10 +1,13 @@ image: Visual Studio 2019 -version: '{build}' +# uncomment to use global ospsuite version (and comment line underneath) +# version: '{build}' +version: '11.1.{build}' # Download script file from GitHub init: - - ps: Update-AppveyorBuild -Version "$($env:ospsuite_version).$($env:appveyor_build_version)" +# uncomment to use global ospsuite version +# - ps: Update-AppveyorBuild -Version "$($env:ospsuite_version).$($env:appveyor_build_version)" - ps: | $ErrorActionPreference = "Stop" Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" @@ -17,12 +20,14 @@ install: platform: x64 + #We use this set of variables to bypass issue with PK-Sim build. This should be removed when dealt with environment: USE_RTOOLS: true NOT_CRAN: true KEEP_VIGNETTES: true R_ARCH: x64 R_VERSION: 4.1.0 + R_CHECK_ARGS: --no-multiarch --no-manual --as-cran COVERALLS_TOKEN: secure: xIz/WZT0ex3bs/CMBJTzzdXLhl3sqfSqJ3MshlSY03pZKuyYQN7Z1FprVgnlFMUZ diff --git a/appveyor.yml b/appveyor.yml index d03acde21..0ba880287 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,14 @@ image: Visual Studio 2019 -version: '{build}' + +# uncomment to use global ospsuite version (and comment line underneath) +# version: '{build}' +version: '11.1.{build}' # Download script file from GitHub init: - - ps: Update-AppveyorBuild -Version "$($env:ospsuite_version).$($env:appveyor_build_version)" +# uncomment to use global ospsuite version +# - ps: Update-AppveyorBuild -Version "$($env:ospsuite_version).$($env:appveyor_build_version)" - ps: | $ErrorActionPreference = "Stop" Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" @@ -19,7 +23,8 @@ environment: USE_RTOOLS: true NOT_CRAN: true KEEP_VIGNETTES: false - R_BUILD_ARGS: --no-build-vignettes --no-manual" + R_BUILD_ARGS: --no-build-vignettes --no-manual + R_CHECK_ARGS: --no-multiarch --no-manual --as-cran R_ARCH: x64 R_VERSION: 4.1.0 #We use this variale to skip some long lasting tests using "skip_on_ci" @@ -31,7 +36,7 @@ build_script: - Rscript -e "install.packages('https://github.com/Open-Systems-Pharmacology/rClr/releases/download/v0.9.1/rClr_0.9.1.zip', repos = NULL, type = 'binary')" - Rscript -e "install.packages('https://ci.appveyor.com/api/projects/open-systems-pharmacology-ci/ospsuite-rutils/artifacts/ospsuite.utils.zip', repos = NULL, type = 'binary')" - Rscript -e "install.packages('https://ci.appveyor.com/api/projects/open-systems-pharmacology-ci/tlf-library/artifacts/tlf.zip', repos = NULL, type = 'binary')" - - Rscript -e "install.packages(c('ggplot2', 'patchwork', 'vdiffr'), repos = 'http://cran.us.r-project.org')" + - Rscript -e "install.packages(c('ggplot2', 'patchwork', 'vdiffr', 'spelling'), repos = 'http://cran.us.r-project.org')" test_script: - travis-tool.sh run_tests @@ -40,6 +45,9 @@ on_failure: - 7z a failure.zip *.Rcheck\* - appveyor PushArtifact failure.zip +on_success: + - Rscript -e "spelling::spell_check_package()" + artifacts: - path: '*.Rcheck\**\*.log' name: Logs diff --git a/dimensions b/dimensions index b5df4d288..a31290e1a 160000 --- a/dimensions +++ b/dimensions @@ -1 +1 @@ -Subproject commit b5df4d288df8a05d3255dadb8268793b86aecd4c +Subproject commit a31290e1a6d49c1f874762e7fec8c909bc230062 diff --git a/docs/404.html b/docs/404.html index 88b08d41c..e927a1df4 100644 --- a/docs/404.html +++ b/docs/404.html @@ -7,8 +7,8 @@ Page not found (404) • ospsuite - - + + License • ospsuiteLicense • ospsuite @@ -10,7 +10,7 @@ ospsuite - 11.0.0 + 11.1.197 + + + + + +
+ + + +
+
+ + + +
+

Introduction +

+

You have already seen how the DataCombined class can be utilized to store observed and/or simulated data (if not, read Working with DataCombined class).

+

Let’s first create a DataCombined object, which we will use to demonstrate different visualizations available.

+
+library(ospsuite)
+
+# simulated data
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+simResults <- runSimulation(sim)
+outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+# observed data
+obsData <- lapply(
+  c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"),
+  function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite"))
+)
+names(obsData) <- lapply(obsData, function(x) x$name)
+
+myDataCombined <- DataCombined$new()
+
+myDataCombined$addSimulationResults(
+  simulationResults = simResults,
+  quantitiesOrPaths = outputPath,
+  groups = "Aciclovir PVB"
+)
+
+myDataCombined$addDataSets(
+  obsData$`Vergin 1995.Iv`,
+  groups = "Aciclovir PVB"
+)
+
+
+

Time profile plots +

+

Time profile plots visualize measured or simulated values against time and help assess if the observed data (represented by symbols and error bars) match the simulated data (represented by lines).

+
+plotIndividualTimeProfile(myDataCombined)
+

+
+
+

Observed versus simulated scatter plot +

+

Observed versus simulated plots allow to assess how far simulated results are from observed values.

+
+plotObservedVsSimulated(myDataCombined)
+

+

The identity line represents perfect correspondence of simulated values with the observed ones. By default, a “two-fold” range is marked by the dashed lines. The “x-fold” range is defined as values that are x-fold higher and 1/x-fold lower than the observed ones. The user can specify multiple ranges by the foldDistance argument.

+
+plotObservedVsSimulated(myDataCombined, foldDistance = c(1.3, 2))
+

+
+
+

Residuals versus time or vs simulated scatter plot +

+

Residual plots show if there is a systematic bias in simulated values either in high-concentration or low-concentration regions, or, alternatively, in early or late time periods.

+
+plotResidualsVsSimulated(myDataCombined)
+

+
+plotResidualsVsTime(myDataCombined)
+

+
+
+

Customizing plots +

+

The look and feel for plots can be customized using the DefaultPlotConfiguration class, which provides various class members that can be used to modify the appearance of the plot.

+
+myPlotConfiguration <- DefaultPlotConfiguration$new()
+
+# Define x units
+myPlotConfiguration$xUnit <- ospUnits$Time$s
+# Define y units
+myPlotConfiguration$yUnit <- ospUnits$`Concentration [mass]`$`µg/l`
+# Change y axis scaling to logarithmic
+myPlotConfiguration$yAxisScale <- tlf::Scaling$log
+
+myPlotConfiguration$title <- "Example: Customizing a Plot"
+myPlotConfiguration$subtitle <- "Using `DefaultPlotConfiguration` class"
+myPlotConfiguration$caption <- "Source: `Aciclovir` data"
+
+myPlotConfiguration$legendPosition <- tlf::LegendPositions$outsideRight
+

This configuration class can be passed to all plotting functions:

+
+plotIndividualTimeProfile(myDataCombined, myPlotConfiguration)
+

+
+
+

Creating multi-panel plots +

+

Each of the plotXXX() returns a ggplot2 object. Lets create different plots from the same DataCombined and store them as variables.

+
+indivProfile <- plotIndividualTimeProfile(myDataCombined, myPlotConfiguration)
+obsVsSim <- plotObservedVsSimulated(myDataCombined, myPlotConfiguration)
+resVsSim <- plotResidualsVsSimulated(myDataCombined)
+resVsTime <- plotResidualsVsTime(myDataCombined)
+

These plots can be combined into a multi-panel figure using the PlotGridConfiguration and then used with plotGrid() function to create a figure.

+
+plotGridConfiguration <- PlotGridConfiguration$new()
+plotGridConfiguration$tagLevels <- "a"
+plotGridConfiguration$title <- "Multiple plots in one figure"
+
+plotGridConfiguration$addPlots(plots = list(indivProfile, obsVsSim, resVsSim, resVsTime))
+
+plotGrid(plotGridConfiguration)
+

+

The function will try to arrange the panels such that the number of rows equals to the number of colums. You can also specify the number of rows or columns through the PlotGridConfiguration:

+
+plotGridConfiguration$nRows <- 1
+
+plotGrid(plotGridConfiguration)
+

+

Check out the documentation of the PlotGridConfiguration class for the list of supported properties.

+
+
+

Saving plots +

+

All plotting functions return ggplot objects that can be further modified.

+

To save a plot to a file, use the ExportConfiguration object. You can edit various properties of the export, including the resolution, file format, or file name.

+
+# Create new export configuration
+exportConfiguration <- tlf::ExportConfiguration$new()
+# Define the path to the folder where the file will be stored
+exportConfiguration$path <- "../OutputFigures"
+# Define the name of the file
+exportConfiguration$name <- "MultiPanelPlot"
+# Resolution
+exportConfiguration$dpi <- 600
+
+# Store the plot into a variable and export it to a file
+plotObject <- plotIndividualTimeProfile(myDataCombined)
+
+exportConfiguration$savePlot(plotObject)
+
+
+

Implementation details +

+

All plotting functions in ospsuite make use of the tlf library to prepare visualizations. To know more about this library, see its website.

+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/articles/data-combined-plotting_files/accessible-code-block-0.0.1/empty-anchor.js b/docs/articles/data-combined-plotting_files/accessible-code-block-0.0.1/empty-anchor.js new file mode 100644 index 000000000..ca349fd6a --- /dev/null +++ b/docs/articles/data-combined-plotting_files/accessible-code-block-0.0.1/empty-anchor.js @@ -0,0 +1,15 @@ +// Hide empty tag within highlighted CodeBlock for screen reader accessibility (see https://github.com/jgm/pandoc/issues/6352#issuecomment-626106786) --> +// v0.0.1 +// Written by JooYoung Seo (jooyoung@psu.edu) and Atsushi Yasumoto on June 1st, 2020. + +document.addEventListener('DOMContentLoaded', function() { + const codeList = document.getElementsByClassName("sourceCode"); + for (var i = 0; i < codeList.length; i++) { + var linkList = codeList[i].getElementsByTagName('a'); + for (var j = 0; j < linkList.length; j++) { + if (linkList[j].innerHTML === "") { + linkList[j].setAttribute('aria-hidden', 'true'); + } + } + } +}); diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-11-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-11-1.png new file mode 100644 index 000000000..1d0204f98 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-11-1.png differ diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-12-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-12-1.png new file mode 100644 index 000000000..0049be3c6 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-12-1.png differ diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-3-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-3-1.png new file mode 100644 index 000000000..f929ac522 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-3-1.png differ diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-4-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-4-1.png new file mode 100644 index 000000000..6e62bd765 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-4-1.png differ diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-5-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-5-1.png new file mode 100644 index 000000000..8d41c9338 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-5-1.png differ diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-6-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-6-1.png new file mode 100644 index 000000000..edbbc02d2 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-7-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-7-1.png new file mode 100644 index 000000000..2a75c17e6 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-7-1.png differ diff --git a/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-9-1.png b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-9-1.png new file mode 100644 index 000000000..ddedba1e9 Binary files /dev/null and b/docs/articles/data-combined-plotting_files/figure-html/unnamed-chunk-9-1.png differ diff --git a/docs/articles/data-combined.html b/docs/articles/data-combined.html new file mode 100644 index 000000000..b44c0ced6 --- /dev/null +++ b/docs/articles/data-combined.html @@ -0,0 +1,425 @@ + + + + + + + + +Working with `DataCombined` class • ospsuite + + + + + + + + + + Skip to contents + + +
+ + + +
+
+ + + +
+

Introduction +

+

In Modeling and Simulation (M&S) workflows, we often need to work with multiple observed and/or simulated datasets. Some of these datasets can be linked together in a group (e.g., the simulated and the corresponding observed data) for analysis and visualization.

+

The DataCombined class in ospsuite provides a container to store data, to group them, and to transform them (e.g. scale or offset). The class accepts data coming from SimulationResults or as a DataSet object (see Observed data).

+

Let’s first generate some simulation results and load observed data.

+
+library(ospsuite)
+
+# Simulation results
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+simResults <- runSimulation(sim)
+outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+# observed data
+obsData <- lapply(
+  c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"),
+  function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite"))
+)
+# Name the elements of the list according to the names of the loaded `DataSet`.
+names(obsData) <- lapply(obsData, function(x) x$name)
+

Typically, DataCombined is used to create figures using the plotXXX() functions. The different plot types are described in Visualizations with DataCombined. To visualize the different functionalities of DataCombined in this document, we will use the plotIndividualTimeProfile() function.

+
+
+

Creating DataCombined object +

+

First, we create a new instance of DataCombined class.

+
+myDataCombined <- DataCombined$new()
+

Then we add simulated results to this object using $addSimulationResults():

+
+myDataCombined$addSimulationResults(
+  simulationResults = simResults,
+  quantitiesOrPaths = outputPath,
+  names = "Aciclovir Plasma",
+  groups = "Aciclovir PVB"
+)
+

Next we add observed data to this object using $addDataSets():

+
+myDataCombined$addDataSets(
+  obsData$`Vergin 1995.Iv`,
+  groups = "Aciclovir PVB"
+)
+

Every data, be it simulated results or from DataSet, must have a unique name within DataCombined. If not specified by user, the path of simulated results or the $name property of the DataSet are used as the name. Alternatively, we can define the name when adding the data, as in the above example adding simulated results.

+

There are a few things to keep in mind here:

+
    +
  • It doesn’t matter in which order observed or simulated datasets are added.
  • +
  • You can add multiple DataSet objects in $addDataSets() method call.
  • +
  • You can add only a single instance of SimulationResults in $addSimulationResults() method call.
  • +
  • If you add a dataset with the same name as existing (e.g., adding simulation results from multiple simulations and not specifying the name, so the path of the results is used as the name), the new data will replace the existing.
  • +
+
+
+

Grouping +

+

Since the DataCombined object can store many datasets, some of them may naturally form a grouping and you would wish to track this in the object. Both $addDataSets() and $addSimulationResults() allow group specification via groups argument.

+

When being plotted, data sets without a grouping will appear with distinct color, line style (for simulated results), or symbol style (for observed data) with a separate entry in the legend, with the name of the data set being the legend entry.

+

Though it is possible to group data with different dimensions, it makes sense to only group data sets that have the same dimension (or dimensions that can be transformed into each other, like Amount and Mass) in order to be able to compare the data. Grouping data sets with different dimensions most likely will result in an error when trying to create plots or calculating residuals with such a DataCombined.

+

Let’s create a DataCombined and add simulation results and observed data without specifying their grouping and create a time profile:

+
+myDataCombined <- DataCombined$new()
+
+myDataCombined$addSimulationResults(
+  simulationResults = simResults,
+  quantitiesOrPaths = outputPath,
+  names = "Aciclovir Plasma"
+)
+
+myDataCombined$addDataSets(
+  obsData$`Vergin 1995.Iv`
+)
+
+plotIndividualTimeProfile(dataCombined = myDataCombined)
+

+

If you do not specify groups when you add datasets, and wish to update groupings later, you can use the $setGroups() method. All data within one group will get one entry in the legend with the name of the group, and will be plotted with the same color.

+
+myDataCombined$setGroups(
+  names = c("Aciclovir Plasma", obsData$`Vergin 1995.Iv`$name),
+  groups = c("Aciclovir PVB", "Aciclovir PVB")
+)
+
+plotIndividualTimeProfile(dataCombined = myDataCombined)
+

+

At any point, you can check the current names and groupings with the following active field:

+
+myDataCombined$groupMap
+#> # A tibble: 2 x 3
+#>   group         name             dataType 
+#>   <chr>         <chr>            <chr>    
+#> 1 Aciclovir PVB Aciclovir Plasma simulated
+#> 2 Aciclovir PVB Vergin 1995.Iv   observed
+
+
+

Transformations +

+

Sometimes the raw data included in DataCombined needs to be transformed using specified offset and scale factor values.

+

This is supported via $setDataTransformations() method, where you can specify the names of datasets, and offsets and scale factors. If these arguments are scalar (i.e., of length 1), then the same value will be applied to all specified names. Scale factors are unitless values by which raw data values will be multiplied, offsets are added to the raw values without any conversion and must therefore be in the same units as the raw data.

+

The internal data frame in DataCombined will be transformed with the specified parameters, with the new columns computed as:

+
    +
  • For x values: newXValue = (rawXValue + xOffset) * xScaleFactor +
  • +
  • For y values: newYValue = (rawYValue + yOffset) * yScaleFactor +
  • +
  • For arithmetic error: newErrorValue = rawErrorValue * abs(yScaleFactor) +
  • +
  • For geometric error: no transformation.
  • +
+

At any point, you can check the applied offsets and scale factors with the following active field:

+
+myDataCombined$dataTransformations
+#> # A tibble: 2 x 5
+#>   name             xOffsets yOffsets xScaleFactors yScaleFactors
+#>   <chr>               <dbl>    <dbl>         <dbl>         <dbl>
+#> 1 Aciclovir Plasma        0        0             1             1
+#> 2 Vergin 1995.Iv          0        0             1             1
+

Now lets take a closer look on possible situations where you would apply any transformations. Consider the observed data from the examples above. It reports concentrations of aciclovir after single intravenous administration.

+
+myDataCombinedTranformations <- DataCombined$new()
+
+myDataCombinedTranformations$addDataSets(
+  obsData$`Vergin 1995.Iv`
+)
+
+plotIndividualTimeProfile(dataCombined = myDataCombinedTranformations)
+

+

However, we might want to use this data set with a simulation where aciclovir is administered 24 hours after simulation begin. To be able to compare simulation results with the data set, we can offset the observed data time by 24 hours. Keep in mind that the offset must be given in the same unit as the data set values are.

+
+# Check the units of the observed time values
+obsData$`Vergin 1995.Iv`$xUnit
+#> [1] "h"
+
+myDataCombinedTranformations$setDataTransformations(
+  forNames = obsData$`Vergin 1995.Iv`$name,
+  xOffsets = 24
+)
+
+plotIndividualTimeProfile(dataCombined = myDataCombinedTranformations)
+

+

In the next step, we want to normalize observed concentrations to a dose. We can easily achieve this with the scale factor. In the next example, we normalize observed values to 250 mg dose by setting the yScaleFactor to 1/250:

+
+myDataCombinedTranformations$setDataTransformations(
+  forNames = obsData$`Vergin 1995.Iv`$name,
+  yScaleFactors = 1 / 250
+)
+
+plotIndividualTimeProfile(dataCombined = myDataCombinedTranformations)
+

Finally, offsetting the observation values might be useful when working with measurements of endogenous substrates, such as the hormone glucagon, and want to correct for the individual specific baseline levels of the hormone.

+
+
+

Extracting a combined data frame +

+

The data frame (also sometimes called as a table) data structure is central to R-based workflows, and, thus, we may wish to extract a data frame for datasets present in the object.

+

Internally, DataCombined extracts data frames for observed and simulated datasets and combines them.

+
+myDataCombined$toDataFrame()
+#> # A tibble: 504 x 27
+#>    name        group dataT~1 xValues xUnit xDime~2 yValues yUnit yDime~3 yErro~4
+#>    <chr>       <chr> <chr>     <dbl> <chr> <chr>     <dbl> <chr> <chr>     <dbl>
+#>  1 Aciclovir ~ Acic~ simula~       0 min   Time       0    µmol~ Concen~      NAm
+#>  2 Aciclovir ~ Acic~ simula~       1 min   Time       3.25 µmol~ Concen~      NAm
+#>  3 Aciclovir ~ Acic~ simula~       2 min   Time       9.10 µmol~ Concen~      NAm
+#>  4 Aciclovir ~ Acic~ simula~       3 min   Time      15.0  µmol~ Concen~      NAm
+#>  5 Aciclovir ~ Acic~ simula~       4 min   Time      20.7  µmol~ Concen~      NAm
+#>  6 Aciclovir ~ Acic~ simula~       5 min   Time      26.2  µmol~ Concen~      NAm
+#>  7 Aciclovir ~ Acic~ simula~       6 min   Time      31.4  µmol~ Concen~      NAm
+#>  8 Aciclovir ~ Acic~ simula~       7 min   Time      36.4  µmol~ Concen~      NAm
+#>  9 Aciclovir ~ Acic~ simula~       8 min   Time      41.1  µmol~ Concen~      NAm
+#> 10 Aciclovir ~ Acic~ simula~       9 min   Time      45.5  µmol~ Concen~      NAm
+#> # ... with 494 more rows, 17 more variables: yErrorType <chr>,
+#> #   yErrorUnit <chr>, IndividualId <int>, molWeight <dbl>, lloq <dbl>,
+#> #   Source <chr>, File <chr>, Sheet <chr>, Molecule <chr>, Species <chr>,
+#> #   Organ <chr>, Compartment <chr>, `Study Id` <chr>, Gender <chr>, Dose <chr>,
+#> #   Route <chr>, `Patient Id` <chr>, and abbreviated variable names
+#> #   1: dataType, 2: xDimension, 3: yDimension, 4: yErrorValues
+

This function returns a tibble data frame. If you wish to modify how it is printed, you can have a look at the available options here. In fact, let’s change a few options and print the data frame again.

+
+options(
+  pillar.width = Inf, # show all columns
+  pillar.min_chars = Inf # to turn off truncation of column titles
+)
+
+myDataCombined$toDataFrame()
+
#> # A tibble: 504 x 27
+#>    name             group         dataType  xValues xUnit xDimension yValues
+#>    <chr>            <chr>         <chr>       <dbl> <chr> <chr>        <dbl>
+#>  1 Aciclovir Plasma Aciclovir PVB simulated       0 min   Time          0   
+#>  2 Aciclovir Plasma Aciclovir PVB simulated       1 min   Time          3.25
+#>  3 Aciclovir Plasma Aciclovir PVB simulated       2 min   Time          9.10
+#>  4 Aciclovir Plasma Aciclovir PVB simulated       3 min   Time         15.0 
+#>  5 Aciclovir Plasma Aciclovir PVB simulated       4 min   Time         20.7 
+#>  6 Aciclovir Plasma Aciclovir PVB simulated       5 min   Time         26.2 
+#>  7 Aciclovir Plasma Aciclovir PVB simulated       6 min   Time         31.4 
+#>  8 Aciclovir Plasma Aciclovir PVB simulated       7 min   Time         36.4 
+#>  9 Aciclovir Plasma Aciclovir PVB simulated       8 min   Time         41.1 
+#> 10 Aciclovir Plasma Aciclovir PVB simulated       9 min   Time         45.5 
+#>    yUnit  yDimension            yErrorValues yErrorType yErrorUnit IndividualId
+#>    <chr>  <chr>                        <dbl> <chr>      <chr>             <int>
+#>  1 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  2 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  3 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  4 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  5 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  6 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  7 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  8 µmol/l Concentration (molar)           NA NA         NA                    00
+#>  9 µmol/l Concentration (molar)           NA NA         NA                    00
+#> 10 µmol/l Concentration (molar)           NA NA         NA                    00
+#>    molWeight  lloq Source File  Sheet Molecule Species Organ Compartment
+#>        <dbl> <dbl> <chr>  <chr> <chr> <chr>    <chr>   <chr> <chr>      
+#>  1      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  2      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  3      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  4      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  5      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  6      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  7      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  8      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  9      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#> 10      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>    `Study Id` Gender Dose  Route `Patient Id`
+#>    <chr>      <chr>  <chr> <chr> <chr>       
+#>  1 NA         NA     NA    NA    NA          
+#>  2 NA         NA     NA    NA    NA          
+#>  3 NA         NA     NA    NA    NA          
+#>  4 NA         NA     NA    NA    NA          
+#>  5 NA         NA     NA    NA    NA          
+#>  6 NA         NA     NA    NA    NA          
+#>  7 NA         NA     NA    NA    NA          
+#>  8 NA         NA     NA    NA    NA          
+#>  9 NA         NA     NA    NA    NA          
+#> 10 NA         NA     NA    NA    NA          
+#> # ... with 494 more rows
+

ospsuite also provides a few helper functions to modify the data frame further.

+

When multiple (observed and/or simulated) datasets are present in a data frame, they are likely to have different units. convertUnits() function helps to convert them to a common unit. The function will not modify the DataCombined object but return a new data frame with unified units.1.

+
+convertUnits(
+  myDataCombined,
+  xUnit = ospUnits$Time$s,
+  yUnit = ospUnits$`Concentration [mass]`$`µg/l``
+)
+#> # A tibble: 504 x 27
+#>    name        group dataT~1 xValues xUnit xDime~2 yValues yUnit yDime~3 yErro~4
+#>    <chr>       <chr> <chr>     <dbl> <chr> <chr>     <dbl> <chr> <chr>     <dbl>
+#>  1 Aciclovir ~ Acic~ simula~       0 s     Time         0  µg/l  Concen~      NAm
+#>  2 Aciclovir ~ Acic~ simula~      60 s     Time       733. µg/l  Concen~      NAm
+#>  3 Aciclovir ~ Acic~ simula~     120 s     Time      2050. µg/l  Concen~      NAm
+#>  4 Aciclovir ~ Acic~ simula~     180 s     Time      3382. µg/l  Concen~      NAm
+#>  5 Aciclovir ~ Acic~ simula~     240 s     Time      4668. µg/l  Concen~      NAm
+#>  6 Aciclovir ~ Acic~ simula~     300 s     Time      5901. µg/l  Concen~      NAm
+#>  7 Aciclovir ~ Acic~ simula~     360 s     Time      7077. µg/l  Concen~      NAm
+#>  8 Aciclovir ~ Acic~ simula~     420 s     Time      8194. µg/l  Concen~      NAm
+#>  9 Aciclovir ~ Acic~ simula~     480 s     Time      9249. µg/l  Concen~      NAm
+#> 10 Aciclovir ~ Acic~ simula~     540 s     Time     10242. µg/l  Concen~      NAm
+#> # ... with 494 more rows, 17 more variables: yErrorType <chr>,
+#> #   yErrorUnit <chr>, IndividualId <int>, molWeight <dbl>, lloq <dbl>,
+#> #   Source <chr>, File <chr>, Sheet <chr>, Molecule <chr>, Species <chr>,
+#> #   Organ <chr>, Compartment <chr>, `Study Id` <chr>, Gender <chr>, Dose <chr>,
+#> #   Route <chr>, `Patient Id` <chr>, and abbreviated variable names
+#> #   1: dataType, 2: xDimension, 3: yDimension, 4: yErrorValues
+
+
+

Further functionalities +

+

Grouping of simulated results with observed data inside DataCombined also allows to calculate the error between simulated results and observations using the calculateResiduals() function. The function calculates the distance between each observed data point within the group and the simulated results. Simulation results interpolated if no simulated value is available for a certain observed time point. Observed data beyond simulated time is ignored, i.e., no extrapolation is performed.

+

There are two ways to calculate the distance (residuals) - linear and logarithmic - specified by the scaling argument. For linear scaling (scaling = tlf::Scaling$lin):

+
+

Residuals are calculated as: Simulation value - Observed value. This means that the residuals are defined by absolute differences.

+
+

For logarithmic scaling (scaling = tlf::Scaling$log):

+
+

Residuals are calculated as: log(Simulation value) - log(Observed value) = log (Simulation Value / Observed Value). This means that the ratio of values is considered which is independent of the magnitude of the value. But for very small observed values, in particular close to 0 values, this can lead to problems, because log(10E-N) = -N can becomes large.

+
+
+# Linear residuals
+calculateResiduals(myDataCombined, scaling = tlf::Scaling$lin)$residualValues
+#>  [1]  9.49304275 -0.14538952  0.17965548  1.84599976  3.52552442  3.64160094
+#>  [7]  3.15654735  2.38392729  1.48703530  0.50532597  0.08009793 -0.14892803
+#> [13] -0.29395086
+
+# Logarithmic residuals
+calculateResiduals(myDataCombined, scaling = tlf::Scaling$log)$residualValues
+#>  [1]  0.104108908 -0.003164673  0.005480074  0.067421107  0.167165355
+#>  [6]  0.209421219  0.251369532  0.250984965  0.281241351  0.198767801
+#> [11]  0.059638034 -0.181845923 -1.001432113
+

To quickly calculate the total error of the DataCombined, one can sum up the absolute values of the residuals:

+
+# Linear residuals
+totalError <- sum(abs(calculateResiduals(myDataCombined, scaling = tlf::Scaling$lin)$residualValues))
+
+print(totalError)
+#> [1] 26.88703
+
+
+

Visualizations with DataCombined +

+

See Visualizations with DataCombined describing functions that can visualize data stored in DataCombined object.

+
+ +
+
+ + + + +
+ + + + + + + diff --git a/docs/articles/data-combined_files/accessible-code-block-0.0.1/empty-anchor.js b/docs/articles/data-combined_files/accessible-code-block-0.0.1/empty-anchor.js new file mode 100644 index 000000000..ca349fd6a --- /dev/null +++ b/docs/articles/data-combined_files/accessible-code-block-0.0.1/empty-anchor.js @@ -0,0 +1,15 @@ +// Hide empty tag within highlighted CodeBlock for screen reader accessibility (see https://github.com/jgm/pandoc/issues/6352#issuecomment-626106786) --> +// v0.0.1 +// Written by JooYoung Seo (jooyoung@psu.edu) and Atsushi Yasumoto on June 1st, 2020. + +document.addEventListener('DOMContentLoaded', function() { + const codeList = document.getElementsByClassName("sourceCode"); + for (var i = 0; i < codeList.length; i++) { + var linkList = codeList[i].getElementsByTagName('a'); + for (var j = 0; j < linkList.length; j++) { + if (linkList[j].innerHTML === "") { + linkList[j].setAttribute('aria-hidden', 'true'); + } + } + } +}); diff --git a/docs/articles/data-combined_files/figure-html/unnamed-chunk-10-1.png b/docs/articles/data-combined_files/figure-html/unnamed-chunk-10-1.png new file mode 100644 index 000000000..3e917fe83 Binary files /dev/null and b/docs/articles/data-combined_files/figure-html/unnamed-chunk-10-1.png differ diff --git a/docs/articles/data-combined_files/figure-html/unnamed-chunk-11-1.png b/docs/articles/data-combined_files/figure-html/unnamed-chunk-11-1.png new file mode 100644 index 000000000..c935af6bb Binary files /dev/null and b/docs/articles/data-combined_files/figure-html/unnamed-chunk-11-1.png differ diff --git a/docs/articles/data-combined_files/figure-html/unnamed-chunk-12-1.png b/docs/articles/data-combined_files/figure-html/unnamed-chunk-12-1.png new file mode 100644 index 000000000..1a9eec923 Binary files /dev/null and b/docs/articles/data-combined_files/figure-html/unnamed-chunk-12-1.png differ diff --git a/docs/articles/data-combined_files/figure-html/unnamed-chunk-6-1.png b/docs/articles/data-combined_files/figure-html/unnamed-chunk-6-1.png new file mode 100644 index 000000000..0453929c7 Binary files /dev/null and b/docs/articles/data-combined_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/articles/data-combined_files/figure-html/unnamed-chunk-7-1.png b/docs/articles/data-combined_files/figure-html/unnamed-chunk-7-1.png new file mode 100644 index 000000000..f929ac522 Binary files /dev/null and b/docs/articles/data-combined_files/figure-html/unnamed-chunk-7-1.png differ diff --git a/docs/articles/efficient-calculations.html b/docs/articles/efficient-calculations.html index 94e92140a..c94cc8fd9 100644 --- a/docs/articles/efficient-calculations.html +++ b/docs/articles/efficient-calculations.html @@ -8,8 +8,8 @@ Efficient calculation • ospsuite - - + + @@ -26,7 +26,7 @@ ospsuite - 11.0.0 + 11.1.197 + + + + + +
+ + + +
+
+ + + +

ospsuite offers a concept of storing and processing numerical x-y data in a unified format by implementing a DataSet class. DataSet objects standardize handling of (observed) data coming from different sources, such as excel files using the data importer functionality of the OSPS, loaded from *.pkml, or manually created. This vignette gives an overview of the options to create DataSet objects and combine them into grouped data sets using the DataCombined class.

+
+

DataSet +

+

A DataSet object stores numerical data pairs - typically time as x values and measurement as y values - and optionally the measurement error . All values have a dimension and a unit (see Dimensions and Units for more information). Furthermore, each DataSet must have a name. When creating a DataSet from scratch (e.g. when the user wants to manually input observed data), a name must be provided:

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Create an empty data set
+dataSet <- DataSet$new("My data set")
+

After creation, the DataSet does not hold any data. The default dimension and unit for the x values is Time and h, respectively. The default dimension and unit for the y values is Concentration (mass) and mg/l, respectively. The dimension of the error values always corresponds to the dimension of the y values, though the units may differ.

+

Setting numerical values (or overwriting current values) is performed by the $setValues() method:

+
+dataSet$setValues(
+  xValues = c(1, 2, 3, 4),
+  yValues = c(0, 0.1, 0.6, 10),
+  yErrorValues = c(0.001, 0.001, 0.1, 1)
+)
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: h 
+#>    Y dimension: Concentration (mass) 
+#>    Y unit: mg/l 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: mg/l 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+

The user can change the dimensions and units of the values. After changing the dimension, the unit is automatically set to the base unit of the dimension. Changing the dimension or unit does not transform the values.

+
+# Print x, y, and error values
+dataSet$xValues
+#> [1] 1 2 3 4
+dataSet$yValues
+#> [1]  0.0  0.1  0.6 10.0
+dataSet$yErrorValues
+#> [1] 0.001 0.001 0.100 1.000
+
+# Change the unit of x-values
+dataSet$xUnit <- ospUnits$Time$min
+# Print the x values - they did not change
+dataSet$xValues
+#> [1] 1 2 3 4
+
+# Change dimension of y-values
+dataSet$yDimension <- ospDimensions$Amount
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: min 
+#>    Y dimension: Amount 
+#>    Y unit: µmol 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: µmol 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+
+# Change the units of y values and error values - they are now different!
+dataSet$yUnit <- ospUnits$Amount$mol
+dataSet$yErrorUnit <- ospUnits$Amount$pmol
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: min 
+#>    Y dimension: Amount 
+#>    Y unit: mol 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: pmol 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+

Two types of error values are supported - arithmetic error (default) and geometric error, the latter being given in fraction. The user can change the error type:

+
+# Default error type is "ArithmeticStdDev"
+dataSet$yErrorType
+#> [1] "ArithmeticStdDev"
+
+# Change error type to geometric
+dataSet$yErrorType <- DataErrorType$GeometricStdDev
+# Error unit is "Unitless" for dimension "Fraction".
+dataSet$yErrorUnit
+#> [1] ""
+
+# Changing error type to arithmetic will set the dimension and unit of the error
+# to the same dimension and unit as the y values
+dataSet$yErrorType <- DataErrorType$ArithmeticStdDev
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: min 
+#>    Y dimension: Amount 
+#>    Y unit: mol 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: mol 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+

A DataSet can store any kind of text meta data as name-values pairs and can be added by the addMetaData() method:

+
+# Add new meta data entries
+dataSet$addMetaData(
+  name = "Molecule",
+  value = "Aciclovir"
+)
+dataSet$addMetaData(
+  name = "Organ",
+  value = "Muscle"
+)
+
+# Print meta data of the DataSet
+print(dataSet$metaData)
+#> $Molecule
+#> [1] "Aciclovir"
+#> 
+#> $Organ
+#> [1] "Muscle"
+

A DataSet or multiple DataSets can be converted to data.frame (or tibble) to be processed in downstream analysis and visualization workflows:

+
+# Create a second data set
+dataSet2 <- DataSet$new(name = "Second data set")
+dataSet2$setValues(
+  xValues = c(1, 2, 3, 4, 5),
+  yValues = c(1, 0, 5, 8, 0.1)
+)
+
+# Convert data sets to a tibble
+myTibble <- dataSetToTibble(dataSets = c(dataSet, dataSet2))
+
+print(myTibble)
+#> # A tibble: 9 x 14
+#>   name      xValues yValues yError~1 xDime~2 xUnit yDime~3 yUnit yErro~4 yErro~5
+#>   <chr>       <dbl>   <dbl>    <dbl> <chr>   <chr> <chr>   <chr> <chr>   <chr>  
+#> 1 My data ~       1   0      0.00100 Time    min   Amount  mol   Arithm~ mol    
+#> 2 My data ~       2   0.1    0.00100 Time    min   Amount  mol   Arithm~ mol    
+#> 3 My data ~       3   0.6    0.1     Time    min   Amount  mol   Arithm~ mol    
+#> 4 My data ~       4  10      1       Time    min   Amount  mol   Arithm~ mol    
+#> 5 Second d~       1   1.00  NA       Time    h     Concen~ mg/l  NA      NA     
+#> 6 Second d~       2   0     NA       Time    h     Concen~ mg/l  NA      NA     
+#> 7 Second d~       3   5.00  NA       Time    h     Concen~ mg/l  NA      NA     
+#> 8 Second d~       4   8.00  NA       Time    h     Concen~ mg/l  NA      NA     
+#> 9 Second d~       5   0.100 NA       Time    h     Concen~ mg/l  NA      NA     
+#> # ... with 4 more variables: molWeight <dbl>, lloq <dbl>, Molecule <chr>,
+#> #   Organ <chr>, and abbreviated variable names 1: yErrorValues, 2: xDimension,
+#> #   3: yDimension, 4: yErrorType, 5: yErrorUnit
+
+
+

Importing data +

+

Creating DataSet objects from scratch is a rather advanced use case. Typically, observed data are loaded either from *.pkml files exported from PK-Sim or MoBi, or imported from Excel files. The function loadDataSetFromPKML() loads data from the *.pkml file. Complementary to this function is the function saveDataSetToPKML() that allows to export any DataSet to a *.pkml that can be loaded e.g. in MoBi.

+
+# Load a data set from PKML
+filePath <- system.file("extdata", "ObsDataAciclovir_1.pkml", package = "ospsuite")
+
+dataSet <- loadDataSetFromPKML(filePath = filePath)
+
+print(dataSet)
+#> DataSet: 
+#>    Name: Vergin 1995.Iv 
+#>    X dimension: Time 
+#>    X unit: h 
+#>    Y dimension: Concentration (mass) 
+#>    Y unit: mg/l 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: mg/l 
+#>    Molecular weight: 225.21 
+#>    LLOQ: 
+#>    Meta data: 
+#> $Source
+#> [1] "X:\\Orga\\BTS-TD\\ET\\TP CSB\\Projects\\Internal Projects\\MagenDarm\\TestSubstanzen\\Acyclovir\\Rohdaten_Acyclovir.xls.Vergin 1995 250 mg iv"
+#> 
+#> $File
+#> [1] "Rohdaten_Acyclovir"
+#> 
+#> $Sheet
+#> [1] "Vergin 1995 250 mg iv"
+#> 
+#> $Molecule
+#> [1] "Aciclovir"
+#> 
+#> $Species
+#> [1] "Human"
+#> 
+#> $Organ
+#> [1] "Peripheral Venous Blood"
+#> 
+#> $Compartment
+#> [1] "Plasma"
+#> 
+#> $`Study Id`
+#> [1] "Vergin 1995"
+#> 
+#> $Gender
+#> [1] "Undefined"
+#> 
+#> $Dose
+#> [1] "250 mg"
+#> 
+#> $Route
+#> [1] "IV"
+#> 
+#> $`Patient Id`
+#> [1] "Iv"
+

Another (and probably the most important) way to create DataSet objects is by importing data from excel files. The function loadDataSetsFromExcel() utilizes the data import functionality implemented in PK-Sim and MoBi and returns a set of DataSet objects. For description of the supported file formats and configurations, please refer to the OSPS documentation.

+

Loading observed data from an Excel sheet requires an ImporterConfiguration. The configuration describes mapping of excel sheet columns to numerical data (e.g.  which column contains the x values) or meta data (e.g., description of the applied dose). One way to obtain such configuration is to create it in PK-Sim or MoBi, save it (as an *.xml) file, and load it in R with the loadDataImporterConfiguration() function:

+
+# Load a configuration from xml file
+filePath <- system.file("extdata", "dataImporterConfiguration.xml", package = "ospsuite")
+importerConfiguration <- loadDataImporterConfiguration(configurationFilePath = filePath)
+
+print(importerConfiguration)
+#> DataImporterConfiguration: 
+#>    Time column: Time [h] 
+#>    Time unit: h 
+#>    Time unit from column: FALSE 
+#>    Measurement column: Concentration (mass)[ng/ml] 
+#>    Measurement unit: ng/ml 
+#>    Measurement unit from column: FALSE 
+#>    Error column: Error [ng/ml] 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: ng/ml 
+#>    Grouping columns: Study Id Organ Compartment Species Gender Molecule Route MW Patient Id Dose [unit] 
+#>    Sheets: 
+#>    Naming pattern: {Source}.{Sheet}.{Study Id}.{Organ}.{Compartment}.{Species}.{Gender}.{Molecule}.{Route}.{Molecular Weight}.{Subject Id}.{Dose}
+

A data importer configuration can also be created from scratch and has to be manually populated by the user. Alternatively, the user can let the software “guess” the configuration for a given excel sheet:

+
+# Excel file
+excelFilePath <- system.file("extdata", "CompiledDataSet.xlsx", package = "ospsuite")
+sheetName <- "TestSheet_1"
+
+# Create importer configuration for the excel sheet
+importerConfiguration_guessed <- createImporterConfigurationForFile(
+  filePath = excelFilePath,
+  sheet = sheetName
+)
+
+print(importerConfiguration)
+#> DataImporterConfiguration: 
+#>    Time column: Time [h] 
+#>    Time unit: h 
+#>    Time unit from column: FALSE 
+#>    Measurement column: Concentration (mass)[ng/ml] 
+#>    Measurement unit: ng/ml 
+#>    Measurement unit from column: FALSE 
+#>    Error column: Error [ng/ml] 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: ng/ml 
+#>    Grouping columns: Study Id Organ Compartment Species Gender Molecule Route MW Patient Id Dose [unit] 
+#>    Sheets: 
+#>    Naming pattern: {Source}.{Sheet}.{Study Id}.{Organ}.{Compartment}.{Species}.{Gender}.{Molecule}.{Route}.{Molecular Weight}.{Subject Id}.{Dose}
+

It is important to manually check the created configuration, as the automated configuration recognition cannot cover all possible cases.

+

If only specific sheets from the excel file should be imported, they can be specified in the ImporterConfiguration. The following example loads the sheets TestSheet_1 and TestSheet_1_withMW:

+
+# Excel file
+excelFilePath <- system.file("extdata", "CompiledDataSet.xlsx", package = "ospsuite")
+sheetName <- "TestSheet_1"
+
+# Create importer configuration for the excel sheet
+importerConfiguration_guessed <- createImporterConfigurationForFile(
+  filePath = excelFilePath,
+  sheet = sheetName
+)
+# Add sheet names to the configuration
+importerConfiguration_guessed$sheets <- c("TestSheet_1", "TestSheet_1_withMW")
+
+# Load data
+dataSets <- loadDataSetsFromExcel(
+  xlsFilePath = excelFilePath,
+  importerConfigurationOrPath = importerConfiguration_guessed
+)
+

Currently, DataImporterConfiguration created from scratch or for a specific data sheet does not support all features of importer configuration, such as specifying the column containing the molecular weight of the measured molecule or the LLOQ values. It is therefore recommended that you use importer configurations created in PK-Si or MoBi.

+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/articles/observed-data_files/accessible-code-block-0.0.1/empty-anchor.js b/docs/articles/observed-data_files/accessible-code-block-0.0.1/empty-anchor.js new file mode 100644 index 000000000..ca349fd6a --- /dev/null +++ b/docs/articles/observed-data_files/accessible-code-block-0.0.1/empty-anchor.js @@ -0,0 +1,15 @@ +// Hide empty tag within highlighted CodeBlock for screen reader accessibility (see https://github.com/jgm/pandoc/issues/6352#issuecomment-626106786) --> +// v0.0.1 +// Written by JooYoung Seo (jooyoung@psu.edu) and Atsushi Yasumoto on June 1st, 2020. + +document.addEventListener('DOMContentLoaded', function() { + const codeList = document.getElementsByClassName("sourceCode"); + for (var i = 0; i < codeList.length; i++) { + var linkList = codeList[i].getElementsByTagName('a'); + for (var j = 0; j < linkList.length; j++) { + if (linkList[j].innerHTML === "") { + linkList[j].setAttribute('aria-hidden', 'true'); + } + } + } +}); diff --git a/docs/articles/ospsuite.html b/docs/articles/ospsuite.html index e9d0aeff6..a85901f71 100644 --- a/docs/articles/ospsuite.html +++ b/docs/articles/ospsuite.html @@ -8,8 +8,8 @@ Introduction to ospsuite • ospsuite - - + + @@ -26,7 +26,7 @@ ospsuite - 11.0.0 + 11.1.197 + + + + + +
+
+
+ +
                     GNU GENERAL PUBLIC LICENSE plus CLARIFYING ADDENDUM
+   
+A. GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your 
+freedom to share and change it. By contrast, the GNU General Public 
+License is intended to guarantee your freedom to share and change free 
+software--to make sure the software is free for all its users. This 
+General Public License applies to most of the Free Software 
+Foundation's software and to any other program whose authors commit to 
+using it. (Some other Free Software Foundation software is covered by 
+the GNU Lesser General Public License instead.) You can apply it to 
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not 
+price. Our General Public Licenses are designed to make sure that you 
+have the freedom to distribute copies of free software (and charge for 
+this service if you wish), that you receive source code or can get it 
+if you want it, that you can change the software or use pieces of it 
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid 
+anyone to deny you these rights or to ask you to surrender the rights. 
+These restrictions translate to certain responsibilities for you if you 
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether 
+gratis or for a fee, you must give the recipients all the rights that 
+you have. You must make sure that they, too, receive or can get the 
+source code. And you must show them these terms so they know their 
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and 
+(2) offer you this license which gives you legal permission to copy, 
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain 
+that everyone understands that there is no warranty for this free 
+software. If the software is modified by someone else and passed on, we 
+want its recipients to know that what they have is not the original, so 
+that any problems introduced by others will not reflect on the original 
+authors' reputations.
+
+Finally, any free program is threatened constantly by software 
+patents. We wish to avoid the danger that redistributors of a free 
+program will individually obtain patent licenses, in effect making the 
+program proprietary. To prevent this, we have made it clear that any 
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and 
+modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains 
+a notice placed by the copyright holder saying it may be distributed 
+under the terms of this General Public License. The “Program”, below, 
+refers to any such program or work, and a “work based on the Program” 
+means either the Program or any derivative work under copyright law: 
+that is to say, a work containing the Program or a portion of it, 
+either verbatim or with modifications and/or translated into another 
+language. (Hereinafter, translation is included without limitation in 
+the term “modification”.) Each licensee is addressed as “you”.
+
+Activities other than copying, distribution and modification are not 
+covered by this License; they are outside its scope. The act of 
+running the Program is not restricted, and the output from the Program 
+is covered only if its contents constitute a work based on the 
+Program (independent of having been made by running the Program). 
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's 
+source code as you receive it, in any medium, provided that you 
+conspicuously and appropriately publish on each copy an appropriate 
+copyright notice and disclaimer of warranty; keep intact all the 
+notices that refer to this License and to the absence of any warranty; 
+and give any other recipients of the Program a copy of this License 
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and 
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion 
+of it, thus forming a work based on the Program, and copy and 
+distribute such modifications or work under the terms of Section 1 
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices 
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in 
+whole or in part contains or is derived from the Program or any 
+part thereof, to be licensed as a whole at no charge to all third 
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively 
+when run, you must cause it, when started running for such 
+interactive use in the most ordinary way, to print or display an 
+announcement including an appropriate copyright notice and a 
+notice that there is no warranty (or else, saying that you provide 
+a warranty) and that users may redistribute the program under 
+these conditions, and telling the user how to view a copy of this 
+License. (Exception: if the Program itself is interactive but 
+does not normally print such an announcement, your work based on 
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If 
+identifiable sections of that work are not derived from the Program, 
+and can be reasonably considered independent and separate works in 
+themselves, then this License, and its terms, do not apply to those 
+sections when you distribute them as separate works. But when you 
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of 
+this License, whose permissions for other licensees extend to the 
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest 
+your rights to work written entirely by you; rather, the intent is to 
+exercise the right to control the distribution of derivative or 
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program 
+with the Program (or with a work based on the Program) on a volume of 
+a storage or distribution medium does not bring the other work under 
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, 
+under Section 2) in object code or executable form under the terms of 
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable 
+source code, which must be distributed under the terms of Sections 
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three 
+years, to give any third party, for a charge no more than your 
+cost of physically performing source distribution, a complete 
+machine-readable copy of the corresponding source code, to be 
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer 
+to distribute corresponding source code. (This alternative is 
+allowed only for noncommercial distribution and only if you 
+received the program in object code or executable form with such 
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for 
+making modifications to it. For an executable work, complete source 
+code means all the source code for all modules it contains, plus any 
+associated interface definition files, plus the scripts used to 
+control compilation and installation of the executable. However, as a 
+special exception, the source code distributed need not include 
+anything that is normally distributed (in either source or binary 
+form) with the major components (compiler, kernel, and so on) of the 
+operating system on which the executable runs, unless that component 
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering 
+access to copy from a designated place, then offering equivalent 
+access to copy the source code from the same place counts as 
+distribution of the source code, even though third parties are not 
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program 
+except as expressly provided under this License. Any attempt 
+otherwise to copy, modify, sublicense or distribute the Program is 
+void, and will automatically terminate your rights under this License. 
+However, parties who have received copies, or rights, from you under 
+this License will not have their licenses terminated so long as such 
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not 
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are 
+prohibited by law if you do not accept this License. Therefore, by 
+modifying or distributing the Program (or any work based on the 
+Program), you indicate your acceptance of this License to do so, and 
+all its terms and conditions for copying, distributing or modifying 
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the 
+Program), the recipient automatically receives a license from the 
+original licensor to copy, distribute or modify the Program subject to 
+these terms and conditions. You may not impose any further 
+restrictions on the recipients' exercise of the rights granted herein. 
+You are not responsible for enforcing compliance by third parties to 
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent 
+infringement or for any other reason (not limited to patent issues), 
+conditions are imposed on you (whether by court order, agreement or 
+otherwise) that contradict the conditions of this License, they do not 
+excuse you from the conditions of this License. If you cannot 
+distribute so as to satisfy simultaneously your obligations under this 
+License and any other pertinent obligations, then as a consequence you 
+may not distribute the Program at all. For example, if a patent 
+license would not permit royalty-free redistribution of the Program by 
+all those who receive copies directly or indirectly through you, then 
+the only way you could satisfy both it and this License would be to 
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under 
+any particular circumstance, the balance of the section is intended to 
+apply and the section as a whole is intended to apply in other 
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any 
+patents or other property right claims or to contest validity of any 
+such claims; this section has the sole purpose of protecting the 
+integrity of the free software distribution system, which is 
+implemented by public license practices. Many people have made 
+generous contributions to the wide range of software distributed 
+through that system in reliance on consistent application of that 
+system; it is up to the author/donor to decide if he or she is willing 
+to distribute software through any other system and a licensee cannot 
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to 
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in 
+certain countries either by patents or by copyrighted interfaces, the 
+original copyright holder who places the Program under this License 
+may add an explicit geographical distribution limitation excluding 
+those countries, so that distribution is permitted only in or among 
+countries not thus excluded. In such case, this License incorporates 
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions 
+of the General Public License from time to time. Such new versions will 
+be similar in spirit to the present version, but may differ in detail to 
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and “any 
+later version”, you have the option of following the terms and conditions 
+either of that version or of any later version published by the Free 
+Software Foundation. If the Program does not specify a version number of 
+this License, you may choose any version ever published by the Free Software 
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free 
+programs whose distribution conditions are different, write to the author 
+to ask for permission. For software which is copyrighted by the Free 
+Software Foundation, write to the Free Software Foundation; we sometimes 
+make exceptions for this. Our decision will be guided by the two goals 
+of preserving the free status of all derivatives of our free software and 
+of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGES.
+
+B. CLARIFYING ADDENDUM 
+
+1. Limitation of Liability
+
+1.1 Limitation for Germany
+
+The following limitation of liability applies for Germany.
+
+The liability of the copyright holder is subject to the law relating to gifts. 
+Thereunder, the copyright holder is subject to a limitation of liability in 
+accordance with Sec. 521, 523, 524, 526 of the German Civil Code (Bürgerliches 
+Gesetzbuch – BGB), with the effect that they are only liable for defects which 
+have been maliciously concealed from the user or which the copyright holder has 
+caused either deliberately or as a result of their gross negligence.
+
+The copyright holder is responsible without limitation insofar as the 
+applicable legal regulations, in particular the German Product Liability 
+Act (Produkthaftungsgesetz – ProdHaftG), require.
+
+In other cases than those set out before, liability is limited for instances
+of gross negligence to a maximum of EUR 10,000.00.
+
+The above limitation on liability also applies accordingly in favor of the 
+employees, agents and representatives of the copyright holder.
+
+1.2 Limitation for Other Countries
+
+Liability for other countries than Germany is excluded in line with the 
+following disclaimer of liability to the extent permitted by law. 
+
+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.
+
+If this disclaimer is invalid in accordance with the law of that country, then 
+liability is excluded to the widest possible extent permitted by law.
+
+2. Copyleft Effect
+
+The GPLv2 contains a strong copyleft clause which requires the licensing of 
+any software you distribute (“your software”) which makes use of software 
+licensed under the GPL (“GPL software”) also to be licensed under the GPL. 
+If you distribute your software (i) containing any of the code of GPL software 
+within it, or (ii) linked to the GPL software, or (iii) otherwise combined with 
+GPL software, note that the copyleft effect will take effect and require the 
+licensing of the program under the GPLv2.
+
+3. Intellectual Property Rights
+
+With the granting of usage rights under the GPLv2, it is not intended to grant 
+any other intellectual property rights such as rights to use trademarks or 
+trade names. The GPLv2 does not mandate the granting of such rights, based on 
+our interpretation of its license conditions and of respective case law (c.f. 
+Judgment of the Düsseldorf Higher Regional Court of the 28th September 2010 – 
+I-20 U 41/09, 20 U 41/09).
+
+ +
+ + +
+ + + + + + + diff --git a/docs/dev/articles/create-individual.html b/docs/dev/articles/create-individual.html new file mode 100644 index 000000000..f77d3d4da --- /dev/null +++ b/docs/dev/articles/create-individual.html @@ -0,0 +1,315 @@ + + + + + + + + +Creating individuals • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +

The ospsuite package provides an interface to the +PK-Sim physiology database to create parameter sets describing a certain +individual. By applying these parameter values to a +simulation, it is possible to simulate different individuals based on +one exported *.pkml simulation. This functionality is only available +when PK-Sim is installed on the system.

+

The easier way to get started is to install the OSPSuite +Setup. This will link PK-Sim to your ospsuite +installation automatically. If the portable version of PK-Sim is used +instead, one must specify the path to the PK-Sim folder by calling the +function initPKSim().

+
+

Creating individuals +

+

The physiology of an individual is defined by the values of the +simulation parameters. To simulate a specific individual, one has to +generate a set of parameter values and apply these values to the model. +NOTE: Currently, only individuals of the same +species as in the original *.pkml simulation can be applied. I.e., if +the simulation exported from PK-Sim represents a human individual, it is +possible to simulate another human individual of different race, gender, +etc., but it is not possible to simulate a rat. Though it is technically +possible, and the simulation will produce some results, these will not +valid.

+

The first step is creating an object describing individual +characteristics. To see the list of available values for the +arguments species, population (only for +human), and gender (only for human), use the enums +Species, HumanPopulation, and +Gender, respectively. This object is then passed to the +function createIndividual to generate a set of parameter +values. The algorithm behind is the same used in PK-Sim when creating an +Individual-Building Block.

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# If no unit is specified, the default units are used. For "weight" it is "kg", for "age" it is "year(s)".
+individualCharacteristics <- createIndividualCharacteristics(
+  species    = Species$Human,
+  population = HumanPopulation$Japanese_Population,
+  gender     = Gender$Female,
+  weight     = 75,
+  height     = 1.75,
+  heightUnit = "m",
+  age        = 43
+)
+print(individualCharacteristics)
+#> IndividualCharacteristics: 
+#>    Species: Human 
+#>    Population: Japanese_Population 
+#>    Gender: FEMALE 
+#>    Age: 43.00 [year(s)] 
+#>    Gestational age: 40.00 [week(s)] 
+#>    Weight: 75.00 [kg] 
+#>    Height: 1.75 [m]
+
+individual <- createIndividual(individualCharacteristics = individualCharacteristics)
+
+# we will not be printing this given the long length of the output, but you can
+# see the details by running:
+# print(individual)
+

The output contains two lists of parameters:

+
    +
  • +distributedParameters: parameters that differ between +the individuals of the selected species
  • +
  • +derivedParameters: parameters defined by formulas in +the simulation
  • +
+

When applying the generated individual parameter set to a simulation +in R, only parameters from the distributedParameters should +be overwritten, otherwise formula dependencies may be destroyed. +Generated parameter values can be conveniently applied using the +setParameterValuesByPath method:

+
+library(ospsuite)
+
+# Load simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+print(simFilePath)
+#> [1] "C:/Temp/RtmpwB6lbo/temp_libpathbf00472d5e59/ospsuite/extdata/Aciclovir.pkml"
+sim <- loadSimulation(simFilePath)
+
+# Apply individual parameters
+setParameterValuesByPath(
+  parameterPaths = individual$distributedParameters$paths,
+  values         = individual$distributedParameters$values,
+  simulation     = sim
+)
+
+
+

Adding enzyme ontogenies +

+

The PK-Sim database includes ontogeny information for some proteins +(see (PK-Sim Ontogeny Database)[https://github.com/Open-Systems-Pharmacology/OSPSuite.Documentation/blob/master/PK-Sim%20Ontogeny%20Database%20Version%207.3.pdf]). +For a protein molecule present in the simulation, it is possible to add +the ontogeny information on one of the predefined proteins. For example, +it is possible to set the ontogeny of the protein MyProtein +to the value of the ontogeny of a CYP3A4 enzyme for the specified +individual. The list of supported ontogenies is stored in the +StandardOntogeny-list.

+
+library(ospsuite)
+
+# All supported ontogenies
+print(StandardOntogeny)
+#> $CYP1A2
+#> [1] "CYP1A2"
+#> 
+#> $CYP2C18
+#> [1] "CYP2C18"
+#> 
+#> $CYP2C19
+#> [1] "CYP2C19"
+#> 
+#> $CYP2C8
+#> [1] "CYP2C8"
+#> 
+#> $CYP2C9
+#> [1] "CYP2C9"
+#> 
+#> $CYP2D6
+#> [1] "CYP2D6"
+#> 
+#> $CYP2E1
+#> [1] "CYP2E1"
+#> 
+#> $CYP3A4
+#> [1] "CYP3A4"
+#> 
+#> $CYP3A5
+#> [1] "CYP3A5"
+#> 
+#> $CYP3A7
+#> [1] "CYP3A7"
+#> 
+#> $UGT1A1
+#> [1] "UGT1A1"
+#> 
+#> $UGT1A4
+#> [1] "UGT1A4"
+#> 
+#> $UGT1A6
+#> [1] "UGT1A6"
+#> 
+#> $UGT1A9
+#> [1] "UGT1A9"
+#> 
+#> $UGT2B4
+#> [1] "UGT2B4"
+#> 
+#> $UGT2B7
+#> [1] "UGT2B7"
+
+# Create the ontogeny for the protein "MyProtein" based on ontology of CYP3A4
+myProteinOntogeny <- MoleculeOntogeny$new(molecule = "MyProtein", ontogeny = StandardOntogeny$CYP3A4)
+print(myProteinOntogeny)
+#> MoleculeOntogeny: 
+#>    Molecule: MyProtein 
+#>    Ontogeny: CYP3A4
+
+# Add this ontogeny to the individual characteristics used to create the individual parameters set
+individualCharacterstics <- createIndividualCharacteristics(
+  species            = Species$Human,
+  population         = HumanPopulation$Japanese_Population,
+  gender             = Gender$Female,
+  weight             = 75,
+  height             = 1.75,
+  heightUnit         = "m",
+  age                = 43,
+  moleculeOntogenies = myProteinOntogeny
+)
+print(individualCharacterstics)
+#> IndividualCharacteristics: 
+#>    Species: Human 
+#>    Population: Japanese_Population 
+#>    Gender: FEMALE 
+#>    Age: 43.00 [year(s)] 
+#>    Gestational age: 40.00 [week(s)] 
+#>    Weight: 75.00 [kg] 
+#>    Height: 1.75 [m] 
+#>    Molecule MyProtein with ontogeny CYP3A4:
+
+individual <- createIndividual(individualCharacteristics = individualCharacterstics)
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/create-run-population.html b/docs/dev/articles/create-run-population.html new file mode 100644 index 000000000..461e4b75e --- /dev/null +++ b/docs/dev/articles/create-run-population.html @@ -0,0 +1,317 @@ + + + + + + + + +Population simulations • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Population simulations +

+

Population simulations can be easily performed in R by combining the +simulation loaded from a *.pkml file with the population information +created in PK-Sim and exported to CSV format (for details, please refer +to OSPS +online documentation) or created directly in R (see Creating populations).

+
+

Loading population file +

+

The method loadPopulation creates an object of the +Population class that can be passed to the +runSimulation() method (see Running simulations and retrieving the +results).

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+# Load population information from csv
+popFilePath <- system.file("extdata", "pop.csv", package = "ospsuite")
+myPopulation <- loadPopulation(csvPopulationFile = popFilePath)
+print(myPopulation)
+#> Population: 
+#>    Number of Individuals: 10
+
+
+
+

Creating populations +

+

Similar to creating individual parameter sets (see Creating individuals), a population is +created from population characteristics created by calling the +method createPopulationCharacteristics(). To see the list +of available values for the arguments species and +population (only for human), use the enums +Species and HumanPopulation, respectively. The +returned object of type PopulationCharacteristics is then +passed to the function createPopulation to generate a set +of parameter values. The algorithm behind is the same used in PK-Sim +when creating an population. Molecule ontogenies can be added as +described in the vignette Creating +individuals.

+
+library(ospsuite)
+
+# If no unit is specified, the default units are used. For "height" it is "dm", for "weight" it is "kg", for "age" it is "year(s)".
+populationCharacteristics <- createPopulationCharacteristics(
+  species             = Species$Human,
+  population          = HumanPopulation$Asian_Tanaka_1996,
+  numberOfIndividuals = 50,
+  proportionOfFemales = 50,
+  weightMin           = 30,
+  weightMax           = 98,
+  weightUnit          = "kg",
+  heightMin           = NULL,
+  heightMax           = NULL,
+  ageMin              = 0,
+  ageMax              = 80,
+  ageUnit             = "year(s)"
+)
+print(populationCharacteristics)
+#> PopulationCharacteristics: 
+#>    Species: Human 
+#>    Population: Asian_Tanaka_1996 
+#>    Number of individuals: 50 
+#>    Proportion of females: 50 
+#>    Age: [0.00 year(s)..80.00 year(s)] 
+#>    Gestational age: ]-Inf..+Inf[ 
+#>    Weight: [30.00 kg..98.00 kg] 
+#>    Height: ]-Inf..+Inf[ 
+#>    BMI: ]-Inf..+Inf[
+
+# Create population from population characteristics
+result <- createPopulation(populationCharacteristics = populationCharacteristics)
+myPopulation <- result$population
+print(myPopulation)
+#> Population: 
+#>    Number of Individuals: 50
+
+
+

Running population simulation +

+

To run a population simulation, the Population object +created by the createPopulation method must be passed to +the runSimulation() method:

+
+library(ospsuite)
+
+# Load simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Run population simulation
+simulationResults <- runSimulation(simulation = sim, population = myPopulation)
+print(simulationResults)
+#> SimulationResults: 
+#>    Number of individuals: 50
+

Population simulations are run in parallel on multi-core machines - +one core simulates a subset of all individuals defined in the +population. By default, the number of cores used equals the maximal +number of logical cores available minus one.

+

The user can change the default behavior by providing custom +SimulationRunOptions().

+
+# Load simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Create a SimulationRunOptions object
+simRunOptions <- SimulationRunOptions$new()
+print(simRunOptions)
+#> SimulationRunOptions: 
+#>    numberOfCores: 15 
+#>    checkForNegativeValues: TRUE 
+#>    showProgress: FALSE
+
+# Change the maximal number of cores to use and show a progress bar during simulation
+simRunOptions$numberOfCores <- 3
+simRunOptions$showProgress <- TRUE
+
+# Run population simulation with custom options
+populationResults <- runSimulation(simulation = sim, population = myPopulation, simulationRunOptions = simRunOptions)
+print(populationResults)
+#> SimulationResults: 
+#>    Number of individuals: 50
+

Simulated time-value pairs for a specific output from the +SimulationResults-object returned by the +runSimulation method can be accessed with the method +getOutputValues. The user can provide either the path(s) of +the output (which can be a molecule, a parameter, or an observer), or +the object(s) of the type Molecule, Parameter, +or Quantity (for observers) with the argument +quantitiesOrPaths. If no output is specified, all outputs +available in the simulation results are returned.

+

The paths of all available outputs can be accessed via

+
+populationResults$allQuantityPaths
+#> [1] "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+

getOutputValues() returns a list with two entries: +data and metadata:

+
    +
  • +data is a dataframe with two predefined columns +(IndividualId and Time) as well as one column for each requested output +
      +
    • IndividualId
    • +
    • +Time a vector with simulated time values (in minutes, +equal for all outputs)
    • +
    • a vector with simulated entries for each output requested.
    • +
    +
  • +
+

The values of IndividualId, Time, and the +simulated outputs, are appended for each simulated individual. Note that +this results in non-monotonously increasing column +Time.

+
+# Get simulated results by path
+resultsPath <- populationResults$allQuantityPaths[[1]]
+print(resultsPath)
+#> [1] "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+resultsData <- getOutputValues(populationResults, quantitiesOrPaths = resultsPath)
+
+resultsTime <- resultsData$data$Time
+resultsValues <- resultsData$data$`Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)`
+
+plot(resultsTime, resultsValues, type = "l")
+

+

To get the results for a specific individual or a set of individuals, +the argument individualIds of the method +getOutputValues() can be specified:

+
+# Get simulated results by path
+resultsPath <- populationResults$allQuantityPaths[[1]]
+print(resultsPath)
+#> [1] "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+# Get only the results for individuals with IDs 1 and 2
+resultsData <- getOutputValues(populationResults, quantitiesOrPaths = resultsPath, individualIds = c(1, 2))
+
+resultsTime <- resultsData$data$Time
+resultsValues <- resultsData$data$`Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)`
+
+plot(resultsTime, resultsValues, type = "l")
+

+

For more information about running simulations, please refer to Running simulations and retrieving the +results.

+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/create-run-population_files/figure-html/getOutputValues-1.png b/docs/dev/articles/create-run-population_files/figure-html/getOutputValues-1.png new file mode 100644 index 000000000..289acace1 Binary files /dev/null and b/docs/dev/articles/create-run-population_files/figure-html/getOutputValues-1.png differ diff --git a/docs/dev/articles/create-run-population_files/figure-html/getOutputValuesForIndividual-1.png b/docs/dev/articles/create-run-population_files/figure-html/getOutputValuesForIndividual-1.png new file mode 100644 index 000000000..98e443f41 Binary files /dev/null and b/docs/dev/articles/create-run-population_files/figure-html/getOutputValuesForIndividual-1.png differ diff --git a/docs/dev/articles/data-combined-plotting.html b/docs/dev/articles/data-combined-plotting.html new file mode 100644 index 000000000..fb783b2fb --- /dev/null +++ b/docs/dev/articles/data-combined-plotting.html @@ -0,0 +1,295 @@ + + + + + + + + +Visualizations with `DataCombined` • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Introduction +

+

You have already seen how the DataCombined class can be +utilized to store observed and/or simulated data (if not, read Working with DataCombined +class).

+

Let’s first create a DataCombined object, which we will +use to demonstrate different visualizations available.

+
+library(ospsuite)
+
+# simulated data
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+simResults <- runSimulation(sim)
+outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+# observed data
+obsData <- lapply(
+  c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"),
+  function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite"))
+)
+names(obsData) <- lapply(obsData, function(x) x$name)
+
+myDataCombined <- DataCombined$new()
+
+myDataCombined$addSimulationResults(
+  simulationResults = simResults,
+  quantitiesOrPaths = outputPath,
+  groups = "Aciclovir PVB"
+)
+
+myDataCombined$addDataSets(
+  obsData$`Vergin 1995.Iv`,
+  groups = "Aciclovir PVB"
+)
+
+
+

Time profile plots +

+

Time profile plots visualize measured or simulated values against +time and help assess if the observed data (represented by symbols and +error bars) match the simulated data (represented by lines).

+
+plotIndividualTimeProfile(myDataCombined)
+

+
+
+

Observed versus simulated scatter plot +

+

Observed versus simulated plots allow to assess how far simulated +results are from observed values.

+
+plotObservedVsSimulated(myDataCombined)
+

+

The identity line represents perfect correspondence of simulated +values with the observed ones. By default, a “two-fold” range is marked +by the dashed lines. The “x-fold” range is defined as values that are +x-fold higher and 1/x-fold lower than the +observed ones. The user can specify multiple ranges by the +foldDistance argument.

+
+plotObservedVsSimulated(myDataCombined, foldDistance = c(1.3, 2))
+

+
+
+

Residuals versus time or vs simulated scatter plot +

+

Residual plots show if there is a systematic bias in simulated values +either in high-concentration or low-concentration regions, or, +alternatively, in early or late time periods.

+
+plotResidualsVsSimulated(myDataCombined)
+

+
+plotResidualsVsTime(myDataCombined)
+

+
+
+

Customizing plots +

+

The look and feel for plots can be customized using the +DefaultPlotConfiguration class, which provides various +class members that can be used to modify the appearance of the +plot.

+
+myPlotConfiguration <- DefaultPlotConfiguration$new()
+
+# Define x units
+myPlotConfiguration$xUnit <- ospUnits$Time$s
+# Define y units
+myPlotConfiguration$yUnit <- ospUnits$`Concentration [mass]`$`µg/l`
+# Change y axis scaling to logarithmic
+myPlotConfiguration$yAxisScale <- tlf::Scaling$log
+
+myPlotConfiguration$title <- "Example: Customizing a Plot"
+myPlotConfiguration$subtitle <- "Using `DefaultPlotConfiguration` class"
+myPlotConfiguration$caption <- "Source: `Aciclovir` data"
+
+myPlotConfiguration$legendPosition <- tlf::LegendPositions$outsideRight
+

This configuration class can be passed to all plotting functions:

+
+plotIndividualTimeProfile(myDataCombined, myPlotConfiguration)
+

+
+
+

Creating multi-panel plots +

+

Each of the plotXXX() returns a ggplot2 +object. Lets create different plots from the same +DataCombined and store them as variables.

+
+indivProfile <- plotIndividualTimeProfile(myDataCombined, myPlotConfiguration)
+obsVsSim <- plotObservedVsSimulated(myDataCombined, myPlotConfiguration)
+resVsSim <- plotResidualsVsSimulated(myDataCombined)
+resVsTime <- plotResidualsVsTime(myDataCombined)
+

These plots can be combined into a multi-panel figure using the +PlotGridConfiguration and then used with +plotGrid() function to create a figure.

+
+plotGridConfiguration <- PlotGridConfiguration$new()
+plotGridConfiguration$tagLevels <- "a"
+plotGridConfiguration$title <- "Multiple plots in one figure"
+
+plotGridConfiguration$addPlots(plots = list(indivProfile, obsVsSim, resVsSim, resVsTime))
+
+plotGrid(plotGridConfiguration)
+

+

The function will try to arrange the panels such that the number of +rows equals to the number of colums. You can also specify the number of +rows or columns through the PlotGridConfiguration:

+
+plotGridConfiguration$nRows <- 1
+
+plotGrid(plotGridConfiguration)
+

+

Check out the documentation of the PlotGridConfiguration +class for the list of supported properties.

+
+
+

Saving plots +

+

All plotting functions return ggplot objects that can be +further modified.

+

To save a plot to a file, use the ExportConfiguration +object. You can edit various properties of the export, including the +resolution, file format, or file name.

+
# Create new export configuration
+exportConfiguration <- tlf::ExportConfiguration$new()
+# Define the path to the folder where the file will be stored
+exportConfiguration$path <- "../OutputFigures"
+# Define the name of the file
+exportConfiguration$name <- "MultiPanelPlot"
+#Resolution
+exportConfiguration$dpi <- 600
+
+# Store the plot into a variable and export it to a file
+plotObject <- plotIndividualTimeProfile(myDataCombined)
+
+exportConfiguration$savePlot(plotObject)
+}
+
+
+

Implementation details +

+

All plotting functions in ospsuite make use of the +tlf library to prepare visualizations. To know more about +this library, see its website.

+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-11-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-11-1.png new file mode 100644 index 000000000..1d0204f98 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-11-1.png differ diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-12-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-12-1.png new file mode 100644 index 000000000..0049be3c6 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-12-1.png differ diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-3-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-3-1.png new file mode 100644 index 000000000..f929ac522 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-3-1.png differ diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-4-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-4-1.png new file mode 100644 index 000000000..6e62bd765 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-4-1.png differ diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-5-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-5-1.png new file mode 100644 index 000000000..8d41c9338 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-5-1.png differ diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-6-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-6-1.png new file mode 100644 index 000000000..edbbc02d2 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-7-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-7-1.png new file mode 100644 index 000000000..2a75c17e6 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-7-1.png differ diff --git a/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-9-1.png b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-9-1.png new file mode 100644 index 000000000..ddedba1e9 Binary files /dev/null and b/docs/dev/articles/data-combined-plotting_files/figure-html/unnamed-chunk-9-1.png differ diff --git a/docs/dev/articles/data-combined.html b/docs/dev/articles/data-combined.html new file mode 100644 index 000000000..4a3300695 --- /dev/null +++ b/docs/dev/articles/data-combined.html @@ -0,0 +1,527 @@ + + + + + + + + +Working with `DataCombined` class • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Introduction +

+

In Modeling and Simulation (M&S) workflows, we often need to work +with multiple observed and/or simulated datasets. Some of these datasets +can be linked together in a group (e.g., the simulated and the +corresponding observed data) for analysis and visualization.

+

The DataCombined class in ospsuite +provides a container to store data, to group them, and to transform them +(e.g. scale or offset). The class accepts data coming from +SimulationResults or as a DataSet object (see +Observed data).

+

Let’s first generate some simulation results and load observed +data.

+
+library(ospsuite)
+
+# Simulation results
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+simResults <- runSimulation(sim)
+outputPath <- "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+# observed data
+obsData <- lapply(
+  c("ObsDataAciclovir_1.pkml", "ObsDataAciclovir_2.pkml", "ObsDataAciclovir_3.pkml"),
+  function(x) loadDataSetFromPKML(system.file("extdata", x, package = "ospsuite"))
+)
+# Name the elements of the list according to the names of the loaded `DataSet`.
+names(obsData) <- lapply(obsData, function(x) x$name)
+

Typically, DataCombined is used to create figures using +the plotXXX() functions. The different plot types are +described in Visualizations with +DataCombined. To visualize the different +functionalities of DataCombined in this document, we will +use the plotIndividualTimeProfile() function.

+
+
+

Creating DataCombined object +

+

First, we create a new instance of DataCombined +class.

+
+myDataCombined <- DataCombined$new()
+

Then we add simulated results to this object using +$addSimulationResults():

+
+myDataCombined$addSimulationResults(
+  simulationResults = simResults,
+  quantitiesOrPaths = outputPath,
+  names = "Aciclovir Plasma",
+  groups = "Aciclovir PVB"
+)
+

Next we add observed data to this object using +$addDataSets():

+
+myDataCombined$addDataSets(
+  obsData$`Vergin 1995.Iv`, 
+  groups = "Aciclovir PVB"
+)
+

Every data, be it simulated results or from DataSet, +must have a unique name within DataCombined. If not +specified by user, the path of simulated results or the +$name property of the DataSet are used as the +name. Alternatively, we can define the name when adding the data, as in +the above example adding simulated results.

+

There are a few things to keep in mind here:

+
    +
  • It doesn’t matter in which order observed or simulated datasets are +added.
  • +
  • You can add multiple DataSet objects in +$addDataSets() method call.
  • +
  • You can add only a single instance of SimulationResults +in $addSimulationResults() method call.
  • +
  • If you add a dataset with the same name as existing (e.g., adding +simulation results from multiple simulations and not specifying the +name, so the path of the results is used as the name), the new data will +replace the existing.
  • +
+
+
+

Grouping +

+

Since the DataCombined object can store many datasets, +some of them may naturally form a grouping and you would wish to track +this in the object. Both $addDataSets() and +$addSimulationResults() allow group specification via +groups argument.

+

When being plotted, data sets without a grouping will appear with +distinct color, line style (for simulated results), or symbol style (for +observed data) with a separate entry in the legend, with the name of the +data set being the legend entry.

+

Though it is possible to group data with different dimensions, it +makes sense to only group data sets that have the same dimension (or +dimensions that can be transformed into each other, like +Amount and Mass) in order to be able to +compare the data. Grouping data sets with different dimensions most +likely will result in an error when trying to create plots or +calculating residuals with such a DataCombined.

+

Let’s create a DataCombined and add simulation results +and observed data without specifying their grouping and create +a time profile:

+
+myDataCombined <- DataCombined$new()
+
+myDataCombined$addSimulationResults(
+  simulationResults = simResults,
+  quantitiesOrPaths = outputPath,
+  names = "Aciclovir Plasma"
+)
+
+myDataCombined$addDataSets(
+  obsData$`Vergin 1995.Iv`
+)
+
+plotIndividualTimeProfile(dataCombined = myDataCombined)
+

+

If you do not specify groups when you add datasets, and +wish to update groupings later, you can use the +$setGroups() method. All data within one group will get one +entry in the legend with the name of the group, and will be plotted with +the same color.

+
+myDataCombined$setGroups(names = c("Aciclovir Plasma", obsData$`Vergin 1995.Iv`$name),
+                         groups = c("Aciclovir PVB", "Aciclovir PVB"))
+
+plotIndividualTimeProfile(dataCombined = myDataCombined)
+

+

At any point, you can check the current names and groupings with the +following active field:

+
+myDataCombined$groupMap
+#> # A tibble: 2 × 3
+#>   group         name             dataType 
+#>   <chr>         <chr>            <chr>    
+#> 1 Aciclovir PVB Aciclovir Plasma simulated
+#> 2 Aciclovir PVB Vergin 1995.Iv   observed
+
+
+

Transformations +

+

Sometimes the raw data included in DataCombined needs to +be transformed using specified offset and scale factor values.

+

This is supported via $setDataTransformations() method, +where you can specify the names of datasets, and offsets and scale +factors. If these arguments are scalar (i.e., of length 1), then the +same value will be applied to all specified names. Scale factors are +unitless values by which raw data values will be multiplied, offsets are +added to the raw values without any conversion and must therefore be in +the same units as the raw data.

+

The internal data frame in DataCombined will be +transformed with the specified parameters, with the new columns computed +as:

+
    +
  • For x values: +newXValue = (rawXValue + xOffset) * xScaleFactor +
  • +
  • For y values: +newYValue = (rawYValue + yOffset) * yScaleFactor +
  • +
  • For arithmetic error: +newErrorValue = rawErrorValue * abs(yScaleFactor) +
  • +
  • For geometric error: no transformation.
  • +
+

At any point, you can check the applied offsets and scale factors +with the following active field:

+
+myDataCombined$dataTransformations
+#> # A tibble: 2 × 5
+#>   name             xOffsets yOffsets xScaleFactors yScaleFactors
+#>   <chr>               <dbl>    <dbl>         <dbl>         <dbl>
+#> 1 Aciclovir Plasma        0        0             1             1
+#> 2 Vergin 1995.Iv          0        0             1             1
+

Now lets take a closer look on possible situations where you would +apply any transformations. Consider the observed data from the examples +above. It reports concentrations of aciclovir after single intravenous +administration.

+
+myDataCombinedTranformations <- DataCombined$new()
+
+myDataCombinedTranformations$addDataSets(
+  obsData$`Vergin 1995.Iv`
+)
+
+plotIndividualTimeProfile(dataCombined = myDataCombinedTranformations)
+

+

However, we might want to use this data set with a simulation where +aciclovir is administered 24 hours after simulation begin. To be able to +compare simulation results with the data set, we can offset the +observed data time by 24 hours. Keep in mind that the offset must be +given in the same unit as the data set values are.

+
+# Check the units of the observed time values
+obsData$`Vergin 1995.Iv`$xUnit
+#> [1] "h"
+
+myDataCombinedTranformations$setDataTransformations(forNames = obsData$`Vergin 1995.Iv`$name,
+                                      xOffsets = 24)
+
+plotIndividualTimeProfile(dataCombined = myDataCombinedTranformations)
+

+

In the next step, we want to normalize observed +concentrations to a dose. We can easily achieve this with the scale +factor. In the next example, we normalize observed values to 250 mg dose +by setting the yScaleFactor to 1/250:

+
+myDataCombinedTranformations$setDataTransformations(forNames = obsData$`Vergin 1995.Iv`$name,
+                                      yScaleFactors = 1/250)
+
+plotIndividualTimeProfile(dataCombined = myDataCombinedTranformations)
+

+Finally, offsetting the observation values might be useful when working +with measurements of endogenous substrates, such as the hormone +glucagon, and want to correct for the individual specific baseline +levels of the hormone.

+
+
+

Extracting a combined data frame +

+

The data frame (also sometimes called as a table) data structure is +central to R-based workflows, and, thus, we may wish to extract a data +frame for datasets present in the object.

+

Internally, DataCombined extracts data frames for +observed and simulated datasets and combines them.

+
+myDataCombined$toDataFrame()
+#> # A tibble: 504 × 27
+#>    name        group dataT…¹ xValues xUnit xDime…² yValues yUnit yDime…³ yErro…⁴
+#>    <chr>       <chr> <chr>     <dbl> <chr> <chr>     <dbl> <chr> <chr>     <dbl>
+#>  1 Aciclovir … Acic… simula…       0 min   Time       0    µmol… Concen…      NA
+#>  2 Aciclovir … Acic… simula…       1 min   Time       3.25 µmol… Concen…      NA
+#>  3 Aciclovir … Acic… simula…       2 min   Time       9.10 µmol… Concen…      NA
+#>  4 Aciclovir … Acic… simula…       3 min   Time      15.0  µmol… Concen…      NA
+#>  5 Aciclovir … Acic… simula…       4 min   Time      20.7  µmol… Concen…      NA
+#>  6 Aciclovir … Acic… simula…       5 min   Time      26.2  µmol… Concen…      NA
+#>  7 Aciclovir … Acic… simula…       6 min   Time      31.4  µmol… Concen…      NA
+#>  8 Aciclovir … Acic… simula…       7 min   Time      36.4  µmol… Concen…      NA
+#>  9 Aciclovir … Acic… simula…       8 min   Time      41.1  µmol… Concen…      NA
+#> 10 Aciclovir … Acic… simula…       9 min   Time      45.5  µmol… Concen…      NA
+#> # … with 494 more rows, 17 more variables: yErrorType <chr>, yErrorUnit <chr>,
+#> #   IndividualId <int>, molWeight <dbl>, lloq <dbl>, Source <chr>, File <chr>,
+#> #   Sheet <chr>, Molecule <chr>, Species <chr>, Organ <chr>, Compartment <chr>,
+#> #   `Study Id` <chr>, Gender <chr>, Dose <chr>, Route <chr>,
+#> #   `Patient Id` <chr>, and abbreviated variable names ¹​dataType, ²​xDimension,
+#> #   ³​yDimension, ⁴​yErrorValues
+

This function returns a tibble +data frame. If you wish to modify how it is printed, you can have a +look at the available options here. +In fact, let’s change a few options and print the data frame again.

+
+options(
+  pillar.width = Inf, # show all columns
+  pillar.min_chars = Inf # to turn off truncation of column titles
+)
+
+myDataCombined$toDataFrame()
+
#> # A tibble: 504 × 27
+#>    name             group         dataType  xValues xUnit xDimension yValues
+#>    <chr>            <chr>         <chr>       <dbl> <chr> <chr>        <dbl>
+#>  1 Aciclovir Plasma Aciclovir PVB simulated       0 min   Time          0   
+#>  2 Aciclovir Plasma Aciclovir PVB simulated       1 min   Time          3.25
+#>  3 Aciclovir Plasma Aciclovir PVB simulated       2 min   Time          9.10
+#>  4 Aciclovir Plasma Aciclovir PVB simulated       3 min   Time         15.0 
+#>  5 Aciclovir Plasma Aciclovir PVB simulated       4 min   Time         20.7 
+#>  6 Aciclovir Plasma Aciclovir PVB simulated       5 min   Time         26.2 
+#>  7 Aciclovir Plasma Aciclovir PVB simulated       6 min   Time         31.4 
+#>  8 Aciclovir Plasma Aciclovir PVB simulated       7 min   Time         36.4 
+#>  9 Aciclovir Plasma Aciclovir PVB simulated       8 min   Time         41.1 
+#> 10 Aciclovir Plasma Aciclovir PVB simulated       9 min   Time         45.5 
+#>    yUnit  yDimension            yErrorValues yErrorType yErrorUnit IndividualId
+#>    <chr>  <chr>                        <dbl> <chr>      <chr>             <int>
+#>  1 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  2 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  3 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  4 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  5 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  6 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  7 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  8 µmol/l Concentration (molar)           NA NA         NA                    0
+#>  9 µmol/l Concentration (molar)           NA NA         NA                    0
+#> 10 µmol/l Concentration (molar)           NA NA         NA                    0
+#>    molWeight  lloq Source File  Sheet Molecule Species Organ Compartment
+#>        <dbl> <dbl> <chr>  <chr> <chr> <chr>    <chr>   <chr> <chr>      
+#>  1      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  2      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  3      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  4      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  5      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  6      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  7      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  8      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>  9      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#> 10      225.    NA NA     NA    NA    NA       NA      NA    NA         
+#>    `Study Id` Gender Dose  Route `Patient Id`
+#>    <chr>      <chr>  <chr> <chr> <chr>       
+#>  1 NA         NA     NA    NA    NA          
+#>  2 NA         NA     NA    NA    NA          
+#>  3 NA         NA     NA    NA    NA          
+#>  4 NA         NA     NA    NA    NA          
+#>  5 NA         NA     NA    NA    NA          
+#>  6 NA         NA     NA    NA    NA          
+#>  7 NA         NA     NA    NA    NA          
+#>  8 NA         NA     NA    NA    NA          
+#>  9 NA         NA     NA    NA    NA          
+#> 10 NA         NA     NA    NA    NA          
+#> # … with 494 more rows
+

ospsuite also provides a few helper functions to +modify the data frame further.

+

When multiple (observed and/or simulated) datasets are present in a +data frame, they are likely to have different units. +convertUnits() function helps to convert them to a common +unit. The function will not modify the DataCombined object +but return a new data frame with unified units.1.

+
+convertUnits(
+  myDataCombined,
+  xUnit = ospUnits$Time$s,
+  yUnit = ospUnits$`Concentration [mass]`$`µg/l`
+)
+#> # A tibble: 504 × 27
+#>    name        group dataT…¹ xValues xUnit xDime…² yValues yUnit yDime…³ yErro…⁴
+#>    <chr>       <chr> <chr>     <dbl> <chr> <chr>     <dbl> <chr> <chr>     <dbl>
+#>  1 Aciclovir … Acic… simula…       0 s     Time         0  µg/l  Concen…      NA
+#>  2 Aciclovir … Acic… simula…      60 s     Time       733. µg/l  Concen…      NA
+#>  3 Aciclovir … Acic… simula…     120 s     Time      2050. µg/l  Concen…      NA
+#>  4 Aciclovir … Acic… simula…     180 s     Time      3382. µg/l  Concen…      NA
+#>  5 Aciclovir … Acic… simula…     240 s     Time      4668. µg/l  Concen…      NA
+#>  6 Aciclovir … Acic… simula…     300 s     Time      5901. µg/l  Concen…      NA
+#>  7 Aciclovir … Acic… simula…     360 s     Time      7077. µg/l  Concen…      NA
+#>  8 Aciclovir … Acic… simula…     420 s     Time      8194. µg/l  Concen…      NA
+#>  9 Aciclovir … Acic… simula…     480 s     Time      9249. µg/l  Concen…      NA
+#> 10 Aciclovir … Acic… simula…     540 s     Time     10242. µg/l  Concen…      NA
+#> # … with 494 more rows, 17 more variables: yErrorType <chr>, yErrorUnit <chr>,
+#> #   IndividualId <int>, molWeight <dbl>, lloq <dbl>, Source <chr>, File <chr>,
+#> #   Sheet <chr>, Molecule <chr>, Species <chr>, Organ <chr>, Compartment <chr>,
+#> #   `Study Id` <chr>, Gender <chr>, Dose <chr>, Route <chr>,
+#> #   `Patient Id` <chr>, and abbreviated variable names ¹​dataType, ²​xDimension,
+#> #   ³​yDimension, ⁴​yErrorValues
+
+
+

Further functionalities +

+

Grouping of simulated results with observed data inside +DataCombined also allows to calculate the error between +simulated results and observations using the +calculateResiduals() function. The function calculates the +distance between each observed data point within the group and the +simulated results. Simulation results interpolated if no simulated value +is available for a certain observed time point. Observed data beyond +simulated time is ignored, i.e., no extrapolation is +performed.

+

There are two ways to calculate the distance (residuals) - linear and +logarithmic - specified by the scaling argument. For linear +scaling (scaling = tlf::Scaling$lin):

+
+

Residuals are calculated as: Simulation value - Observed value. This +means that the residuals are defined by absolute differences.

+
+

For logarithmic scaling +(scaling = tlf::Scaling$log):

+
+

Residuals are calculated as: log(Simulation value) - log(Observed +value) = log (Simulation Value / Observed Value). This means that the +ratio of values is considered which is independent of the magnitude of +the value. But for very small observed values, in particular close to 0 +values, this can lead to problems, because log(10E-N) = -N can becomes +large.

+
+
+#Linear residuals
+calculateResiduals(myDataCombined, scaling = tlf::Scaling$lin)$residualValues
+#>  [1]  9.49304275 -0.14538952  0.17965548  1.84599976  3.52552442  3.64160094
+#>  [7]  3.15654735  2.38392729  1.48703530  0.50532597  0.08009793 -0.14892803
+#> [13] -0.29395086
+
+#Logarithmic residuals
+calculateResiduals(myDataCombined, scaling = tlf::Scaling$log)$residualValues
+#>  [1]  0.239719620 -0.007286928  0.012618336  0.155242835  0.384912455
+#>  [6]  0.482210178  0.578799737  0.577914238  0.647582143  0.457679775
+#> [11]  0.137321647 -0.418715711 -2.305882655
+

To quickly calculate the total error of the +DataCombined, one can sum up the absolute values of the +residuals:

+
+#Linear residuals
+totalError <- sum(abs(calculateResiduals(myDataCombined, scaling = tlf::Scaling$lin)$residualValues))
+
+print(totalError)
+#> [1] 26.88703
+
+
+

Visualizations with DataCombined +

+

See Visualizations with +DataCombined describing functions that can visualize +data stored in DataCombined object.

+
+ +
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-10-1.png b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-10-1.png new file mode 100644 index 000000000..3e917fe83 Binary files /dev/null and b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-10-1.png differ diff --git a/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-11-1.png b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-11-1.png new file mode 100644 index 000000000..c935af6bb Binary files /dev/null and b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-11-1.png differ diff --git a/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-12-1.png b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-12-1.png new file mode 100644 index 000000000..3a976f081 Binary files /dev/null and b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-12-1.png differ diff --git a/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-6-1.png b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-6-1.png new file mode 100644 index 000000000..0453929c7 Binary files /dev/null and b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-7-1.png b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-7-1.png new file mode 100644 index 000000000..f929ac522 Binary files /dev/null and b/docs/dev/articles/data-combined_files/figure-html/unnamed-chunk-7-1.png differ diff --git a/docs/dev/articles/efficient-calculations.html b/docs/dev/articles/efficient-calculations.html new file mode 100644 index 000000000..c98f8bf5c --- /dev/null +++ b/docs/dev/articles/efficient-calculations.html @@ -0,0 +1,359 @@ + + + + + + + + +Efficient calculation • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +

There are a few ways to run simulations using this package. The +easiest way is, as expected, not necessarily the most efficient one. So +depending on the use case, a different approach than what is outlined in +Run Simulations might be more +suited.

+

This vignette aims at describing some scenarios and the suggested +methods to efficiently run simulations.

+
+

Life cycle of a simulation run +

+

In general, a user loads a simulation, updates some parameter values, +calculates the simulation and retrieves the results. Here are some steps +inherent to each simulation:

+
    +
  1. Loading a simulation
  2. +
  3. Optional: Setting parameter values
  4. +
  5. Optional: Setting initial values
  6. +
  7. Initializing the simulation engine
  8. +
  9. Solving the ODE System (calculating the outputs)
  10. +
+

Depending on the simulation of interest, some steps might take much +longer than others. For example, a simulation with multiple +administrations simulated over many months will load and initialize +quickly (1 and 4) while calculating (5) will be the critical step. On +the other hand, a simulation with dozens of compounds will also take +time to load and initialize (1 and 4).

+
+
+

Running a single simulation once +

+

This is by far the easiest use cases. A simulation is loaded from a +.pkml file and the outputs are calculated. In this use case, step 4 and +5 are automatically lumped together in one single call (See Run Simulations for more details).

+
+
+

Running a single simulation multiple times and varying some +parameters/initial values between each run +

+

This is already a use case where the optimal and most efficient way +depends drastically on the simulation at hand. Let’s assume that we want +to calculate the outputs of a simulation for different doses.

+

A simple implementation could follow the following idea, where a +simulation is loaded once, the dose value is updated and the simulation +is calculated for each dose of interest.

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Load and run the simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+doseParameter <- getAllParametersMatching(toPathString("Applications", "**", "Dose"), sim)[[1]]
+
+# run for dose 100mg
+doseParameter$value <- toBaseUnit(doseParameter, 100, "mg")
+result100 <- runSimulations(simulations = sim)
+
+# run for dose 200mg
+doseParameter$value <- toBaseUnit(doseParameter, 200, "mg")
+result200 <- runSimulations(simulations = sim)
+
+# ...
+
+# run for dose 500mg
+doseParameter$value <- toBaseUnit(doseParameter, 500, "mg")
+result500 <- runSimulations(simulations = sim)
+

This implementation is fairly easy to understand, but has potentially +one big performance bottleneck. Each simulation run is performed +sequentially (i.e. one at a time). That means that +there will not be any performance boost from having multiple cores on +the machine. If the simulation only takes a few seconds to run, this +solution is absolutely acceptable. However, if we assume that a +simulation takes 1h to run, this code would take over 5h to complete, as +each runSimulations call would be executed +sequentially.

+

When the simulation run time (5) is much greater than the loading (1) +and initialization time (4), a better approach is to load the simulation +multiple times and run the simulation concurrently (i.e. in +parallel).

+

Consider the following implementation:

+
+library(ospsuite)
+# Load and run the simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+
+loadSimulationWithDose <- function(doseInMg) {
+  sim <- loadSimulation(simFilePath, loadFromCache = FALSE)
+  doseParameter <- getAllParametersMatching(toPathString("Applications", "**", "Dose"), sim)[[1]]
+  doseParameter$value <- toBaseUnit(doseParameter, doseInMg, "mg")
+  return(sim)
+}
+
+# Creates 5 instances of a simulation (This is very fast for typical simulations)
+sim100 <- loadSimulationWithDose(doseInMg = 100)
+sim200 <- loadSimulationWithDose(doseInMg = 200)
+sim300 <- loadSimulationWithDose(doseInMg = 300)
+sim400 <- loadSimulationWithDose(doseInMg = 400)
+sim500 <- loadSimulationWithDose(doseInMg = 500)
+
+
+# Runs the simulation in parallel
+results <- runSimulations(simulations = list(sim100, sim200, sim300, sim400, sim500))
+
+# Results in now a list of SimulationResults
+

This implementation is also fairly straight forward. We load the +simulation multiple times from the same file (note the usage of +loadFromCache = FALSE to ensure that we always get a new +instance of a simulation and not the same instance) and we set the dose. +Then, we call the runSimulations function with the list of +simulations. The engine will run these simulations in parallel, which +means that the overall execution time should be slightly more than 1h, +as each run would be executed at the same time.

+
+
+

Optimized loading for running simulations with predefined set of +variable parameters +

+

This is a more advanced use case, typically when implementing some +kind of optimization or sensitivity algorithms. The +ospsuite package introduces the concept of a +simulation batch. With a simulation batch, the variable +parameters (or initial values) of a simulation to vary between each run +are specified explicitly during the creation of the simulation batch. +This allows the simulation engine to speed up the system significantly +as some equations can be rewritten and simplified. A simulation batch +also keeps the simplified simulation in memory. That means that running +a simulation again with a new set of values will be much faster, as the +initialization phase (4) is only done if required and not for every +run.

+

Consider the following implementation. We first create two simulation +batches for the same model and define the parameter values that can be +changed between runs. Note that, as opposed to the +Simulation object, we do not have access to any +parameter/species values nor can we change the outputs or simulation +time.

+
+library(ospsuite)
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+
+# We load the simulation for which the batches will be created
+sim1 <- loadSimulation(simFilePath, loadFromCache = FALSE)
+
+# define the list of parameter that will be varied between the runs.
+# For the first batch, we will vary 2 parameters: Lipophilicity and Permeability
+parameterPaths <- c("Aciclovir|Lipophilicity", "Aciclovir|Permeability")
+
+# define a first simulation batch
+simBatch1 <- createSimulationBatch(simulation = sim1, parametersOrPaths = parameterPaths)
+
+# for the second batch, we will vary Molecular Weight
+simBatch2 <- createSimulationBatch(
+  simulation = sim1,
+  parametersOrPaths = c("Aciclovir|Molecular weight")
+)
+
+simulationBatches <- list(simBatch1, simBatch2)
+

In the next step, we define multiple runs of the simulation batch by +adding different parameter (and/or species start) values set. Each set +of values correspond to one run. The benefit of this approach is that +the initialization steps (1 and 4) are only performed once. After that, +only the execution time (step 5) will have an impact on the total +performance.

+
+# now setting some parameter run values (the size of the array should match
+# the number of parameters to vary for each batch
+simBatch1$addRunValues(parameterValues = c(1, 2))
+#> [1] "8a3564c3-787c-4612-934a-7d2d9fc9c5d8"
+simBatch1$addRunValues(parameterValues = c(3, 4))
+#> [1] "6308deb5-1c44-4bf5-89e3-c8c09faca1ff"
+simBatch1$addRunValues(parameterValues = c(5, 6))
+#> [1] "2ff0dcd4-ace2-425d-83ac-4580a071241a"
+
+# We only have one parameter to vary for simBatch2, therefore only one value to set
+simBatch2$addRunValues(parameterValues = 150)
+#> [1] "ce053ce3-97e1-4b8e-9aa1-a2c3ffb4fd9f"
+simBatch2$addRunValues(parameterValues = 200)
+#> [1] "9e3554bf-1b52-47c4-b4b8-a2c41efc2047"
+simBatch2$addRunValues(parameterValues = 300)
+#> [1] "fbe1a8cd-29d4-4253-a034-fdd1c7147cfd"
+simBatch2$addRunValues(parameterValues = 400)
+#> [1] "ab5d195b-8217-4e6c-aacb-a69b3380804e"
+

So far, we created 2 simulation batches, one with 3 parameter sets +and the other one with 4. That means that 3 runs will be enqueued for +simBatch1 and 4 will be enqueued for simBatch2. Each run gets a unique +id that can later be used to correctly assign simulation results to the +simulated set of parameters.

+
+# Now we run the simulation batches.
+# By doing so, 7 runs (3 for simBatch1 and 4 for simBatch2) will be executed in parallel.
+# Please see documentation of runSimulationBatches for more details.
+# The resulting output is a named list, where the names are the ids of the enqueued runs.
+results <- runSimulationBatches(simulationBatches)
+print(names(unlist(results)))
+#> [1] "6f6b5318-31a0-4ad3-987d-f96f2e779e23.8a3564c3-787c-4612-934a-7d2d9fc9c5d8"
+#> [2] "6f6b5318-31a0-4ad3-987d-f96f2e779e23.6308deb5-1c44-4bf5-89e3-c8c09faca1ff"
+#> [3] "6f6b5318-31a0-4ad3-987d-f96f2e779e23.2ff0dcd4-ace2-425d-83ac-4580a071241a"
+#> [4] "a0b630cf-3303-4976-9ef3-761d176e7322.ce053ce3-97e1-4b8e-9aa1-a2c3ffb4fd9f"
+#> [5] "a0b630cf-3303-4976-9ef3-761d176e7322.9e3554bf-1b52-47c4-b4b8-a2c41efc2047"
+#> [6] "a0b630cf-3303-4976-9ef3-761d176e7322.fbe1a8cd-29d4-4253-a034-fdd1c7147cfd"
+#> [7] "a0b630cf-3303-4976-9ef3-761d176e7322.ab5d195b-8217-4e6c-aacb-a69b3380804e"
+

The enqueued run values are cleared after calling +runSimulationBatches(), so executing the run again would +result in an empty results list. We can now set more values to the +batches and run them again. Notes: - We do not have to always set the +same number of values at the same time - Previous runValues +are automatically cleared when runSimulationBatches() is +called.

+
+simBatch1$addRunValues(parameterValues = c(10, 20))
+#> [1] "6e7a6105-52bf-41dd-a674-bdbcc5d1d365"
+simBatch1$addRunValues(parameterValues = c(30, 40))
+#> [1] "c1ca69e5-a9f8-4ede-9439-0349f1fef3d8"
+simBatch2$addRunValues(parameterValues = 500)
+#> [1] "391c2d90-7fc5-4e8f-8191-ae3ae76928ae"
+simBatch2$addRunValues(parameterValues = 200)
+#> [1] "cb47f955-359a-4f0c-869b-f4ae6b135af8"
+
+# this run will be much faster as the simulation won't be initialized again.
+# Only the new value will be set as specified when adding new run values with addRunValues
+results2 <- runSimulationBatches(simulationBatches)
+
+# Once the simulation batches instances are not needed anymore, they can be removed
+# from the environment and the allocated memory cleared.
+# This will be done automatically when the R session is terminated.
+rm(simBatch1)
+rm(simBatch2)
+

Usage of SimulationBatch is recommended for advanced +scenarios where simulations expected to be run hundreds of times and +where each second that can be spared will impact the performance +significantly.

+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/index.html b/docs/dev/articles/index.html new file mode 100644 index 000000000..8ce08bf9d --- /dev/null +++ b/docs/dev/articles/index.html @@ -0,0 +1,125 @@ + +Articles • ospsuite + Skip to contents + + +
+
+
+ + +
+

Miscellaneous

+

Salad of various things

+ +
Efficient calculation
+
+
Table parameters
+
+
Dimensions and Units
+
+
+
+ + +
+ + + + + + + diff --git a/docs/dev/articles/load-get.html b/docs/dev/articles/load-get.html new file mode 100644 index 000000000..2623210ee --- /dev/null +++ b/docs/dev/articles/load-get.html @@ -0,0 +1,352 @@ + + + + + + + + +Loading a simulation and accessing entities • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Loading a simulation +

+

In general, every workflow starts with loading a simulation by +calling the loadSimulation() function. The function +receives the full path to the *.pkml file format +exported from PK-Sim or MoBi, and returns the corresponding simulation +object.

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+
+sim <- loadSimulation(simFilePath)
+
+
+

Accessing entities of the model and their properties - the path +concept +

+

Once the simulation is loaded, it is possible to retrieve various +entities of the model. The most important entities are +containers, molecules, and +parameters. The methods getContainer, +getMolecule, and getParameter search for the +respective entity with the given path located under a +container. path is a string where the elements +of the path (i.e., containers in the hierarchy of the simulation) are +separated by |. container is an instance of +the Container-class within the model structure the path is +relative to. In most cases, container is the +Simulation object created by calling +loadSimulation(pkmlSimulationFile).

+
+# Get the molecule Aciclovir located in kidney intracellular space
+moleculeInKid <- getMolecule("Organism|Kidney|Intracellular|Aciclovir", sim)
+print(moleculeInKid)
+#> Molecule: 
+#>    Path: Organism|Kidney|Intracellular|Aciclovir 
+#>    Initial Value: 0.00 [µmol]
+
+# Get the container "Liver"
+livContainer <- getContainer("Organism|Liver", sim)
+print(livContainer)
+#> Container: 
+#>    Container type: Organ 
+#>    Path: Organism|Liver
+
+# Get the parameter volume of the liver interstitial space
+# Note that the path used is relative to the liver container
+livParam <- getParameter("Interstitial|Volume", livContainer)
+print(livParam)
+#> Parameter: 
+#>    Path: Organism|Liver|Interstitial|Volume 
+#>    Value: 0.35 [l] 
+#>    isFormula: TRUE 
+#>    formula: f_int*V 
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+

The functions getAllContainersMatching(), +getAllMoleculesMatching(), and +getAllParametersMatching() return a list of objects +representing all entities whose paths match the generic paths provided +in the list paths located under container. +Generic paths are constructed by using the wildcard symbols +* (exactly one occurrence of any element) or +** (zero or more occurrences of any element).

+
+# Get the parameter `Volume` of the intracellular space of all organs,
+# with exactly one path element before `Intracellular`
+volumeParams <- getAllParametersMatching("Organism|*|Intracellular|Volume", sim)
+length(volumeParams)
+#> [1] 15
+# The PBPK model has 15 organs with an "Intracellular" sub-compartment
+
+# Get the parameter `Volume` of the intracellular space of all organs,
+# no matter how many sub-containers the organ has
+volumeParams <- getAllParametersMatching("Organism|**|Intracellular|Volume", sim)
+length(volumeParams)
+#> [1] 28
+# The list also includes parameters of organs like "Liver|Periportal",
+# or the mucosal compartments of the intestine.
+

Note that the path +"Organism|Kidney|*|Intracellular|Volume" will return no +parameters in the standard models, as there are no sub-containers +between Kidney and Intracellular. In contrast, +"Organism|Kidney|**|Intracellular|Volume" is a valid +path.

+

The functions getAllContainersMatching(), +getAllMoleculesMatching(), and +getAllParametersMatching() can also be used to retrieve +entities from multiple paths:

+
+# Get the molecule Aciclovir located in `Liver|Periportal|Intracellular` and `VenousBlood|Plasma`
+molecules <- getAllMoleculesMatching(c(
+  "Organism|VenousBlood|Plasma|Aciclovir",
+  "Organism|Liver|Periportal|Intracellular|Aciclovir"
+), sim)
+print(molecules)
+#> [[1]]
+#> Molecule: 
+#>    Path: Organism|Liver|Periportal|Intracellular|Aciclovir 
+#>    Initial Value: 0.00 [µmol] 
+#> 
+#> [[2]]
+#> Molecule: 
+#>    Path: Organism|VenousBlood|Plasma|Aciclovir 
+#>    Initial Value: 0.00 [µmol]
+

The entities possess various properties that can be accessed through +their objects. The most important properties for a container are:

+
+# Path of the container
+livContainer$path
+#> [1] "Organism|Liver"
+
+# Parent container
+livContainer$parentContainer
+#> Container: 
+#>    Container type: Organism 
+#>    Path: Organism
+

The most important properties for a molecule are:

+
+# Initial value of the molecule
+moleculeInKid$value
+#> [1] 0
+
+# Dimension of the molecule. See section "Unit conversion" for more information.
+moleculeInKid$dimension
+#> [1] "Amount"
+
+# Is the initial value defined by a formula?
+moleculeInKid$isFormula
+#> [1] FALSE
+
+# Type of the formula. CONSTANT if the value is defined by a constant.
+moleculeInKid$formula
+#> Formula: 
+#>    isConstant: TRUE
+

The most important properties for a parameter are:

+
+# Initial value of the parameter
+livParam$value
+#> [1] 0.3533005
+
+# Dimension of the parameter. See section "Unit conversion" for more information.
+livParam$dimension
+#> [1] "Volume"
+
+# Base unit of the parameter. See section "Unit conversion" for more information.
+livParam$unit
+#> [1] "l"
+
+# Is the initial value defined by a formula?
+livParam$isFormula
+#> [1] TRUE
+
+# Type of the formula. CONSTANT if the value is defined by a constant.
+livParam$formula
+#> Formula: 
+#>    isFormula: TRUE 
+#>    formula: f_int*V
+
+
+

Loading simulation tree +

+

A convenient way to traverse the simulation structure is given by the +method getSimulationTree. The method generates a tree-like +list of all paths within the simulation. Each element of the tree +contains all the sub-containers of the element. The final elements are +strings representing the path to the entity.

+
+# Load simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Create simulation tree
+simTree <- getSimulationTree(sim)
+
+# Calling simTree would list all entities within the simulation
+# (given how long this tree can be, we will skip it here, but you can try
+# running the following command in your console)
+# simTree
+
+# Accessing the parameter "Organism|Weight"
+simTree$Organism$Weight
+#> $path
+#> [1] "Organism|Weight"
+
+# Getting all entities located under "Organism|Liver|Periportal|Intracellular"
+entitiesList <- simTree$Organism$Liver$Periportal$Intracellular
+entitiesList
+#> $`Volume of protein container`
+#> $`Volume of protein container`$path
+#> [1] "Organism|Liver|Periportal|Intracellular|Volume of protein container"
+#> 
+#> 
+#> $Volume
+#> $Volume$path
+#> [1] "Organism|Liver|Periportal|Intracellular|Volume"
+#> 
+#> 
+#> $pH
+#> $pH$path
+#> [1] "Organism|Liver|Periportal|Intracellular|pH"
+#> 
+#> 
+#> $Aciclovir
+#> $Aciclovir$path
+#> [1] "Organism|Liver|Periportal|Intracellular|Aciclovir"
+#> 
+#> $Aciclovir$Concentration
+#> $Aciclovir$Concentration$path
+#> [1] "Organism|Liver|Periportal|Intracellular|Aciclovir|Concentration"
+#> 
+#> 
+#> $Aciclovir$`Partition coefficient (water/container)`
+#> $Aciclovir$`Partition coefficient (water/container)`$path
+#> [1] "Organism|Liver|Periportal|Intracellular|Aciclovir|Partition coefficient (water/container)"
+#> 
+#> 
+#> $Aciclovir$`Concentration in container`
+#> $Aciclovir$`Concentration in container`$path
+#> [1] "Organism|Liver|Periportal|Intracellular|Aciclovir|Concentration in container"
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/observed-data.html b/docs/dev/articles/observed-data.html new file mode 100644 index 000000000..55c2d00d7 --- /dev/null +++ b/docs/dev/articles/observed-data.html @@ -0,0 +1,473 @@ + + + + + + + + +Observed data • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +

ospsuite offers a concept of storing and processing +numerical x-y data in a unified format by implementing a +DataSet class. DataSet objects standardize +handling of (observed) data coming from different sources, such as excel +files using the data importer functionality of the OSPS, loaded from +*.pkml, or manually created. This vignette gives an +overview of the options to create DataSet objects and +combine them into grouped data sets using the DataCombined class.

+
+

DataSet +

+

A DataSet object stores numerical data pairs - typically +time as x values and measurement as y values - and optionally the +measurement error . All values have a dimension and a +unit (see Dimensions and +Units for more information). Furthermore, each DataSet +must have a name. When creating a DataSet from +scratch (e.g. when the user wants to manually input observed data), a +name must be provided:

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Create an empty data set
+dataSet <- DataSet$new("My data set")
+

After creation, the DataSet does not hold any data. The +default dimension and unit for the x values is Time and +h, respectively. The default dimension and unit for the y +values is Concentration (mass) and mg/l, +respectively. The dimension of the error values always corresponds to +the dimension of the y values, though the units may differ.

+

Setting numerical values (or overwriting current values) is performed +by the $setValues() method:

+
+dataSet$setValues(
+  xValues = c(1, 2, 3, 4),
+  yValues = c(0, 0.1, 0.6, 10),
+  yErrorValues = c(0.001, 0.001, 0.1, 1)
+)
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: h 
+#>    Y dimension: Concentration (mass) 
+#>    Y unit: mg/l 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: mg/l 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+

The user can change the dimensions and units of the values. After +changing the dimension, the unit is automatically set to the base +unit of the dimension. Changing the dimension or unit does +not transform the values.

+
+# Print x, y, and error values
+dataSet$xValues
+#> [1] 1 2 3 4
+dataSet$yValues
+#> [1]  0.0  0.1  0.6 10.0
+dataSet$yErrorValues
+#> [1] 0.001 0.001 0.100 1.000
+
+# Change the unit of x-values
+dataSet$xUnit <- ospUnits$Time$min
+# Print the x values - they did not change
+dataSet$xValues
+#> [1] 1 2 3 4
+
+# Change dimension of y-values
+dataSet$yDimension <- ospDimensions$Amount
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: min 
+#>    Y dimension: Amount 
+#>    Y unit: µmol 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: µmol 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+
+# Change the units of y values and error values - they are now different!
+dataSet$yUnit <- ospUnits$Amount$mol
+dataSet$yErrorUnit <- ospUnits$Amount$pmol
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: min 
+#>    Y dimension: Amount 
+#>    Y unit: mol 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: pmol 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+

Two types of error values are supported - arithmetic error (default) +and geometric error, the latter being given in fraction. The user can +change the error type:

+
+# Default error type is "ArithmeticStdDev"
+dataSet$yErrorType
+#> [1] "ArithmeticStdDev"
+
+# Change error type to geometric
+dataSet$yErrorType <- DataErrorType$GeometricStdDev
+# Error unit is "Unitless" for dimension "Fraction".
+dataSet$yErrorUnit
+#> [1] ""
+
+# Changing error type to arithmetic will set the dimension and unit of the error
+# to the same dimension and unit as the y values
+dataSet$yErrorType <- DataErrorType$ArithmeticStdDev
+
+print(dataSet)
+#> DataSet: 
+#>    Name: My data set 
+#>    X dimension: Time 
+#>    X unit: min 
+#>    Y dimension: Amount 
+#>    Y unit: mol 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: mol 
+#>    Molecular weight: 
+#>    LLOQ: 
+#>    Meta data: 
+#> list()
+

A DataSet can store any kind of text meta data as +name-values pairs and can be added by the addMetaData() +method:

+
+# Add new meta data entries
+dataSet$addMetaData(
+  name = "Molecule",
+  value = "Aciclovir"
+)
+dataSet$addMetaData(
+  name = "Organ",
+  value = "Muscle"
+)
+
+# Print meta data of the DataSet
+print(dataSet$metaData)
+#> $Molecule
+#> [1] "Aciclovir"
+#> 
+#> $Organ
+#> [1] "Muscle"
+

A DataSet or multiple DataSets can be +converted to data.frame (or tibble) to be +processed in downstream analysis and visualization workflows:

+
+# Create a second data set
+dataSet2 <- DataSet$new(name = "Second data set")
+dataSet2$setValues(
+  xValues = c(1, 2, 3, 4, 5),
+  yValues = c(1, 0, 5, 8, 0.1)
+)
+
+# Convert data sets to a tibble
+myTibble <- dataSetToTibble(dataSets = c(dataSet, dataSet2))
+
+print(myTibble)
+#> # A tibble: 9 × 14
+#>   name      xValues yValues yError…¹ xDime…² xUnit yDime…³ yUnit yErro…⁴ yErro…⁵
+#>   <chr>       <dbl>   <dbl>    <dbl> <chr>   <chr> <chr>   <chr> <chr>   <chr>  
+#> 1 My data …       1   0      0.00100 Time    min   Amount  mol   Arithm… mol    
+#> 2 My data …       2   0.1    0.00100 Time    min   Amount  mol   Arithm… mol    
+#> 3 My data …       3   0.6    0.1     Time    min   Amount  mol   Arithm… mol    
+#> 4 My data …       4  10      1       Time    min   Amount  mol   Arithm… mol    
+#> 5 Second d…       1   1.00  NA       Time    h     Concen… mg/l  NA      NA     
+#> 6 Second d…       2   0     NA       Time    h     Concen… mg/l  NA      NA     
+#> 7 Second d…       3   5.00  NA       Time    h     Concen… mg/l  NA      NA     
+#> 8 Second d…       4   8.00  NA       Time    h     Concen… mg/l  NA      NA     
+#> 9 Second d…       5   0.100 NA       Time    h     Concen… mg/l  NA      NA     
+#> # … with 4 more variables: molWeight <dbl>, lloq <dbl>, Molecule <chr>,
+#> #   Organ <chr>, and abbreviated variable names ¹​yErrorValues, ²​xDimension,
+#> #   ³​yDimension, ⁴​yErrorType, ⁵​yErrorUnit
+
+
+

Importing data +

+

Creating DataSet objects from scratch is a rather +advanced use case. Typically, observed data are loaded either from +*.pkml files exported from PK-Sim or MoBi, or imported from +Excel files. The function loadDataSetFromPKML() loads data +from the *.pkml file. Complementary to this function is the +function saveDataSetToPKML() that allows to export any +DataSet to a *.pkml that can be loaded e.g. in +MoBi.

+
+# Load a data set from PKML
+filePath <- system.file("extdata", "ObsDataAciclovir_1.pkml", package = "ospsuite")
+
+dataSet <- loadDataSetFromPKML(filePath = filePath)
+
+print(dataSet)
+#> DataSet: 
+#>    Name: Vergin 1995.Iv 
+#>    X dimension: Time 
+#>    X unit: h 
+#>    Y dimension: Concentration (mass) 
+#>    Y unit: mg/l 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: mg/l 
+#>    Molecular weight: 225.21 
+#>    LLOQ: 
+#>    Meta data: 
+#> $Source
+#> [1] "X:\\Orga\\BTS-TD\\ET\\TP CSB\\Projects\\Internal Projects\\MagenDarm\\TestSubstanzen\\Acyclovir\\Rohdaten_Acyclovir.xls.Vergin 1995 250 mg iv"
+#> 
+#> $File
+#> [1] "Rohdaten_Acyclovir"
+#> 
+#> $Sheet
+#> [1] "Vergin 1995 250 mg iv"
+#> 
+#> $Molecule
+#> [1] "Aciclovir"
+#> 
+#> $Species
+#> [1] "Human"
+#> 
+#> $Organ
+#> [1] "Peripheral Venous Blood"
+#> 
+#> $Compartment
+#> [1] "Plasma"
+#> 
+#> $`Study Id`
+#> [1] "Vergin 1995"
+#> 
+#> $Gender
+#> [1] "Undefined"
+#> 
+#> $Dose
+#> [1] "250 mg"
+#> 
+#> $Route
+#> [1] "IV"
+#> 
+#> $`Patient Id`
+#> [1] "Iv"
+

Another (and probably the most important) way to create +DataSet objects is by importing data from excel files. The +function loadDataSetsFromExcel() utilizes the data import +functionality implemented in PK-Sim and MoBi and returns a set of +DataSet objects. For description of the supported file +formats and configurations, please refer to the OSPS +documentation.

+

Loading observed data from an Excel sheet requires an +ImporterConfiguration. The configuration describes mapping +of excel sheet columns to numerical data (e.g.  which column contains +the x values) or meta data (e.g., description of the applied dose). One +way to obtain such configuration is to create it in PK-Sim or MoBi, save +it (as an *.xml) file, and load it in R with the +loadDataImporterConfiguration() function:

+
+# Load a configuration from xml file
+filePath <- system.file("extdata", "dataImporterConfiguration.xml", package = "ospsuite")
+importerConfiguration <- loadDataImporterConfiguration(configurationFilePath = filePath)
+
+print(importerConfiguration)
+#> DataImporterConfiguration: 
+#>    Time column: Time [h] 
+#>    Time unit: h 
+#>    Time unit from column: FALSE 
+#>    Measurement column: Concentration (mass)[ng/ml] 
+#>    Measurement unit: ng/ml 
+#>    Measurement unit from column: FALSE 
+#>    Error column: Error [ng/ml] 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: ng/ml 
+#>    Grouping columns: Study Id Organ Compartment Species Gender Molecule Route MW Patient Id Dose [unit] 
+#>    Sheets: 
+#>    Naming pattern: {Source}.{Sheet}.{Study Id}.{Organ}.{Compartment}.{Species}.{Gender}.{Molecule}.{Route}.{Molecular Weight}.{Subject Id}.{Dose}
+

A data importer configuration can also be created from scratch and +has to be manually populated by the user. Alternatively, the user can +let the software “guess” the configuration for a given excel sheet:

+
+# Excel file
+excelFilePath <- system.file("extdata", "CompiledDataSet.xlsx", package = "ospsuite")
+sheetName <- "TestSheet_1"
+
+# Create importer configuration for the excel sheet
+importerConfiguration_guessed <- createImporterConfigurationForFile(
+  filePath = excelFilePath,
+  sheet = sheetName
+)
+
+print(importerConfiguration)
+#> DataImporterConfiguration: 
+#>    Time column: Time [h] 
+#>    Time unit: h 
+#>    Time unit from column: FALSE 
+#>    Measurement column: Concentration (mass)[ng/ml] 
+#>    Measurement unit: ng/ml 
+#>    Measurement unit from column: FALSE 
+#>    Error column: Error [ng/ml] 
+#>    Error type: ArithmeticStdDev 
+#>    Error unit: ng/ml 
+#>    Grouping columns: Study Id Organ Compartment Species Gender Molecule Route MW Patient Id Dose [unit] 
+#>    Sheets: 
+#>    Naming pattern: {Source}.{Sheet}.{Study Id}.{Organ}.{Compartment}.{Species}.{Gender}.{Molecule}.{Route}.{Molecular Weight}.{Subject Id}.{Dose}
+

It is important to manually check the created configuration, as the +automated configuration recognition cannot cover all possible cases.

+

If only specific sheets from the excel file should be imported, they +can be specified in the ImporterConfiguration. The +following example loads the sheets TestSheet_1 and +TestSheet_1_withMW:

+
+# Excel file
+excelFilePath <- system.file("extdata", "CompiledDataSet.xlsx", package = "ospsuite")
+sheetName <- "TestSheet_1"
+
+# Create importer configuration for the excel sheet
+importerConfiguration_guessed <- createImporterConfigurationForFile(
+  filePath = excelFilePath,
+  sheet = sheetName
+)
+# Add sheet names to the configuration
+importerConfiguration_guessed$sheets <- c("TestSheet_1", "TestSheet_1_withMW")
+
+# Load data
+dataSets <- loadDataSetsFromExcel(
+  xlsFilePath = excelFilePath,
+  importerConfigurationOrPath = importerConfiguration_guessed
+)
+

Currently, DataImporterConfiguration created from +scratch or for a specific data sheet does not support all features of +importer configuration, such as specifying the column containing the +molecular weight of the measured molecule or the LLOQ values. It is +therefore recommended that you use importer configurations created in +PK-Si or MoBi.

+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/ospsuite.html b/docs/dev/articles/ospsuite.html new file mode 100644 index 000000000..79e4727f5 --- /dev/null +++ b/docs/dev/articles/ospsuite.html @@ -0,0 +1,415 @@ + + + + + + + + +Introduction to ospsuite • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +

The ospsuite R-package is part of the Open Systems +Pharmacology Software (OSPS), an open-source suite of modeling and +simulation tools for pharmaceutical and other life-sciences +applications. This package provides the functionality of loading, +manipulating, and simulating the simulations created in the software +tools PK-Sim and MoBi. This document gives an overview of the general +workflow and the most important methods.

+
+

General information +

+

In order to load a simulation in R, it must be present in the +*.pkml file format. Every simulation in PK-Sim or MoBi +can be exported to the *.pkml file. Unless otherwise stated, the +examples shown in the vignettes are based on the Aciclovir example +model. The model can be found in the PK-Sim examples folder of the OSPS +installation.

+

The general workflow with the ospsuite package can be +summarized in following steps:

+
    +
  1. Loading a simulation from .pkml file.
  2. +
  3. Accessing entities of the simulation, such as molecules, parameters, +or containers, and reading information about them.
  4. +
  5. Changing simulation settings, parameter values, and molecules start +values.
  6. +
  7. Retrieving the results and processing them.
  8. +
+

The workflow steps are described in the following vignettes:

+ +

Some aspects of the ospsuite package may appear +uncommon for the users not familiar with the object-oriented approach. +It is recommended to read the following section to better understand +some semantics and to get the most of the flexibility and efficiency of +the package.

+
+
+

Object-oriented approach +

+

The ospsuite R-package utilizes the concept of object +oriented (OO) programming based on the R6 system. While the +philosophy of the package is to offer a functional programming workflow +more common for the R users, it is important to understand some basic +concepts of the OO programming.

+

Most of the functions implemented in ospsuite return +an instance (or an object) of a class. These +objects can be used as inputs for other functions. Additionally, each +object offers a set of properties (which can be themselves other +objects) and functions, accessible by the $ sign:

+
+object1           <- ClassName$new()
+aProperty         <- object1$property1
+resultOfAFunction <- object1$multiply(1,2)
+

Important information about the object can be printed out by calling +print(object).

+
+
The most important classes are:
+
+
+
Simulation
+
+Representation of the simulation loaded from the *.pkml file. +
+
+
+
+
+
SimulationRunOptions
+
+An object defining the options of a simulation run. The options are: +numberOfCores the maximal number of (logical) cores that +can be used by the (population) simulation; +checkForNegativeValues a boolean defining if an error if +thrown if some variables become negative for which the +"negativeValuesAllowed"-flag is set to FALSE +(can be used to ignore numerical noise); showProgress a +Boolean if a “progress bar” is shown in the console representing the +progress of the simulation. +
+
+
+
+
+
SolverSettings
+
+Object defining the settings of the solver. Stored in +SimulationSettings of a Simulation (accessibly +by field $settings). +
+
+
+
+
+
OutputSchema
+
+Definition of the output intervals of the simulation. The +OutputSchema defines the total simulation time and the time +points at which results are generated. Can be accessed as property of a +Simulation-object. +
+
+
+
+
+
OutputSelections
+
+List of quantities (parameters and molecules) for which the outputs will +be generated. Can be accessed as property of a +Simulation-object. +
+
+
+
+
+
SimulationResults
+
+Results of a simulation, either individual or population. Holds the +simulated values for all quantities defined in the +OutputSelections. See Running +a simulation for more information. +
+
+
+
+
+
Entity
+
+Every accessible distinct part of the model. Most prominent entities are +Molecule, Parameter, Container. +Every Entity has properties $path representing +the path within the model structure and $parentContainer +being the container this entity is located in. +
+
+
+
+
+
Container
+
+A Container is an element of model structure that contains +other entities (e.g., spatial containers, molecules, parameters). The +most prominent containers are organs and compartments. A loaded +Simulation is also a container. +
+
+
+
+
+
Quantity
+
+An Entity in the simulation that has a value - namely +Molecule and Parameter. Every +Quantity has a $value and a +$dimension. Further important fields are +$unit, which is the base unit of the dimension. See Dimensions and Units for more +information. +
+
+
+
+
+
Molecule
+
+A molecule located in a Container. The +$value-property refers to the initial value in the +simulation. Inherits from Quantity. +
+
+
+
+
+
Parameter
+
+A parameter. The $value-property refers to the initial +value in the simulation. Inherits from Quantity. +
+
+
+
+
+
Formula
+
+The value of each Quantity is described by a +$formula-property. There are different types of formulae, +see Changing parameter and molecule start +values for more information. +
+
+
+
+
+
IndividualCharacteristics
+
+An object used for creating of individual parameter sets with the +createIndividual-method. See Creating individuals for more +information. +
+
+
+
+
+
Population
+
+An object describing a virtual population. Can be either loaded from a +*.csv-file created by PK-Sim or created with the +createPopulation-method. See Population simulations for more +information. +
+
+
+
+
+
PopulationCharacteristics
+
+An object used for creating of population parameter sets with the +createPopulation-method. See Population simulations for more +information. +
+
+
+
+
+
SimulationPKAnalyses
+
+PK analyses for a simulations result. +
+
+
+
+
+
QuantityPKParameter
+
+A certain PK parameter for an output quantity. Has the fields +$name which is the name of the PK parameter (e.g. “C_max”), +$quantityPath the path of the output the parameter has been +calculated for, and $values the value (or list of values +for a population simulation). +
+
+
+
+
+
SensitivityAnalysis
+
+A class defining the analysis of which input parameters have most impact +on the output curves of a simulation. See Sensitivity analysis for more +information. +
+
+
+
+
+
SensitivityAnalysisResults
+
+Results of running the SensitivityAnalysis +
+
+
+
+
+
PKParameterSensitivity
+
+The sensitivity (field $value) of a PK-Parameter +($pkParameterName) for the output $outputPath +calculated for the varied parameter $parameterName. See Sensitivity analysis for more +information. +
+
+
+
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/pk-analysis.html b/docs/dev/articles/pk-analysis.html new file mode 100644 index 000000000..fc3f7c01c --- /dev/null +++ b/docs/dev/articles/pk-analysis.html @@ -0,0 +1,457 @@ + + + + + + + + +Calculating PK parameters of simulation outputs • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Calculating PK parameters +

+

The ospsuite package gives you access to the standard +PK Parameters calculated by PK-Sim such as AUC, Cmax etc. For the +complete list of PK Parameters supported out of the box, please refer to +the online +documentation.

+

PK parameters can be calculated for all outputs of a simulation. +First, simulation results must be calculated, and the +SimulationResults object is then passed to the method +calculatePKAnalyses. For the list of calculated PK +parameters and their description, please refer to OSPS +documentation.

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Load the simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Run the simulation
+simulationResults <- runSimulation(simulation = sim)
+
+# Calculate PK-analyses
+pkAnalysis <- calculatePKAnalyses(results = simulationResults)
+
+# Get the path of the simulated output
+outputPath <- simulationResults$allQuantityPaths[[1]]
+print(outputPath)
+#> [1] "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+# Get all calculated PK parameters for the output path
+allPkParams <- pkAnalysis$allPKParametersFor(outputPath)
+print(allPkParams)
+#> [[1]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): C_max 
+#>    Dimension: Concentration (molar) 
+#>    Unit: µmol/l 
+#> 
+#> [[2]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): C_max_norm 
+#>    Dimension: Concentration (mass) 
+#>    Unit: kg/l 
+#> 
+#> [[3]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): t_max 
+#>    Dimension: Time 
+#>    Unit: min 
+#> 
+#> [[4]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): C_tEnd 
+#>    Dimension: Concentration (molar) 
+#>    Unit: µmol/l 
+#> 
+#> [[5]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): AUC_tEnd 
+#>    Dimension: AUC (molar) 
+#>    Unit: µmol*min/l 
+#> 
+#> [[6]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): AUC_tEnd_norm 
+#>    Dimension: AUC (mass) 
+#>    Unit: kg*min/l 
+#> 
+#> [[7]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): AUC_inf 
+#>    Dimension: AUC (molar) 
+#>    Unit: µmol*min/l 
+#> 
+#> [[8]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): AUC_inf_norm 
+#>    Dimension: AUC (mass) 
+#>    Unit: kg*min/l 
+#> 
+#> [[9]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): MRT 
+#>    Dimension: Time 
+#>    Unit: min 
+#> 
+#> [[10]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): Thalf 
+#>    Dimension: Time 
+#>    Unit: min 
+#> 
+#> [[11]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): FractionAucLastToInf 
+#>    Dimension: Fraction 
+#>    Unit:  
+#> 
+#> [[12]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): CL 
+#>    Dimension: Flow per weight 
+#>    Unit: l/min/kg 
+#> 
+#> [[13]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): Vss 
+#>    Dimension: Volume per body weight 
+#>    Unit: l/kg 
+#> 
+#> [[14]]
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): Vd 
+#>    Dimension: Volume per body weight 
+#>    Unit: l/kg
+
+# Get C_max parameter
+c_max <- pkAnalysis$pKParameterFor(quantityPath = outputPath, pkParameter = "C_max")
+c_max
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): C_max 
+#>    Dimension: Concentration (molar) 
+#>    Unit: µmol/l
+

The function calculatePKAnalyses returns an object of +the class SimulationPKAnalyses, which allows to retrieve +either a certain PK parameter for an output path, or all calculated PK +parameters for an output path.

+

The methods $allPKParametersFor() and +$pKParameterFor() return a object (or a list of objects) of +the class QuantityPKParameter, with the fields +$name which is the name of the pk-parameter (e.g. “C_max”), +$quantityPath the path of the output the parameter has been +calculated for, and $values the value (or list of values +for a population simulation).

+
+# Get C_max parameter
+c_max <- pkAnalysis$pKParameterFor(quantityPath = outputPath, pkParameter = "C_max")
+
+print(c_max)
+#> QuantityPKParameter: 
+#>    Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood): C_max 
+#>    Dimension: Concentration (molar) 
+#>    Unit: µmol/l
+
+c_max$name
+#> [1] "C_max"
+c_max$quantityPath
+#> [1] "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+c_max$values
+#> [1] 50.25272
+

In case of a population simulation, $values return a +list of values calculated for each individual.

+
+
+

Extracting a dataframe +

+

Sometimes it is desirable to have the calculated parameters in a +dataframe for working with them further in other workflows (e.g. data +wrangling), and the package provides a convenient helper to extract +it:

+
+pkAnalysesToDataFrame(pkAnalysis)
+#> # A tibble: 14 × 5
+#>    IndividualId QuantityPath                              Param…¹    Value Unit 
+#>           <int> <chr>                                     <chr>      <dbl> <chr>
+#>  1            0 Organism|PeripheralVenousBlood|Aciclovir… C_max   5.03e+ 1 µmol…
+#>  2            0 Organism|PeripheralVenousBlood|Aciclovir… C_max_… 3.07e+ 6 mg/l 
+#>  3            0 Organism|PeripheralVenousBlood|Aciclovir… t_max   1.83e- 1 h    
+#>  4            0 Organism|PeripheralVenousBlood|Aciclovir… C_tEnd  3.25e- 2 µmol…
+#>  5            0 Organism|PeripheralVenousBlood|Aciclovir… AUC_tE… 4.06e+ 3 µmol…
+#>  6            0 Organism|PeripheralVenousBlood|Aciclovir… AUC_tE… 2.48e+11 µg*m…
+#>  7            0 Organism|PeripheralVenousBlood|Aciclovir… AUC_inf 4.07e+ 3 µmol…
+#>  8            0 Organism|PeripheralVenousBlood|Aciclovir… AUC_in… 2.48e+11 µg*m…
+#>  9            0 Organism|PeripheralVenousBlood|Aciclovir… MRT     3.22e+ 0 h    
+#> 10            0 Organism|PeripheralVenousBlood|Aciclovir… Thalf   3.02e+ 0 h    
+#> 11            0 Organism|PeripheralVenousBlood|Aciclovir… Fracti… 2.09e- 3 NA   
+#> 12            0 Organism|PeripheralVenousBlood|Aciclovir… CL      4.02e+ 0 ml/m…
+#> 13            0 Organism|PeripheralVenousBlood|Aciclovir… Vss     7.78e+ 2 ml/kg
+#> 14            0 Organism|PeripheralVenousBlood|Aciclovir… Vd      1.05e+ 3 ml/kg
+#> # … with abbreviated variable name ¹​Parameter
+
+
+

Import and export of PK-analyses +

+

Population analysis calculated in R can be exported to a *.csv file +and loaded in PK-Sim, and vice versa.

+
+# Load and run the simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+simulationResults <- runSimulation(simulation = sim)
+
+# Calculate PK-analysis
+pkAnalysis <- calculatePKAnalyses(results = simulationResults)
+
+# Export to csv
+csvPKAnalysisPath <- system.file("extdata", "PKAnalyses.csv", package = "ospsuite")
+exportPKAnalysesToCSV(pkAnalyses = pkAnalysis, filePath = csvPKAnalysisPath)
+
+# Load from csv
+pkAnalysisLoaded <- importPKAnalysesFromCSV(filePath = csvPKAnalysisPath, simulation = sim)
+
+
+

User-Defined PK Parameters +

+

The ospsuite package also supports user-defined PK +Parameters, e.g. PK Parameter that can be tailored to specific project +needs. This feature is useful when calculating PK Parameters for +specific time intervals, or to apply PK Parameters for output not using +the dimension Concentration.

+

A User-Defined PK Parameter is always based on an existing +PK-Parameter and simply extends the way the output is calculated. It is +not possible at the moment to define your own formula.

+

The utility addUserDefinedPKParameter +creates and returns an instance of the UserDefinedPKParameter +which can then be parameterized for specific requirements.

+
+

Examples +

+
+

Calculate AUC between for a specific time interval. +

+
+# Adds a user defined parameter named `MyAuc` that will calculate the value of AUC between t=50 min and t=80min only.
+
+# Create a new parameter based on the standard AUC parameter
+myAUC <- addUserDefinedPKParameter(
+  name = "MyAUC",
+  standardPKParameter = StandardPKParameter$AUC_tEnd
+)
+
+# Specifies start time and end time in minute
+myAUC$startTime <- 50
+myAUC$endTime <- 80
+
+
+

Calculate CMax between the 4th and 5th application of a multiple +applications simulation. +

+
+# Adds a user defined parameter named `MyCMax` that will calculate the value of Cmax between the 4th and 5th application
+
+# Create a new parameter based on the standard C_max parameter
+myCMax <- addUserDefinedPKParameter(
+  name = "MyCMax",
+  standardPKParameter = StandardPKParameter$C_max
+)
+
+# Specifies start application (4th) and end applicaiton (5th)
+myCMax$startApplicationIndex <- 4
+myCMax$endApplicationIndex <- 5
+
+
+

Calculate CMax between the 4th and 5th application of a multiple +applications simulation and apply a time offset +

+
+# Adds a user defined parameter named `MyCMax` that will calculate the value of Cmax between the 4th application start time + 10 min and
+# the 5th application start time + 20min
+
+# Create a new parameter based on the standard C_max parameter
+myCMax <- addUserDefinedPKParameter(
+  name = "MyCMax",
+  standardPKParameter = StandardPKParameter$C_max
+)
+
+# Specifies start application (4th) and end applicaiton (5th)
+myCMax$startApplicationIndex <- 4
+myCMax$endApplicationIndex <- 5
+
+# Specifies start time offset. The PK calculations wil effectively start at StartTime of 4th Application + 10 min
+myCMax$startTimeOffset <- 10
+
+# Specifies end time offset . The PK calculations wil effectively ends at StartTime of 5th Application + 20 min
+myCMax$endTimeOffset <- 20
+
+
+

Update Dimension of a standard parameter for correct +visualization +

+

PK parameters currently always assume that the underlying quantity is +a concentration and do not check for units. If the quantity of interest +has another dimension, creating a user-defined PK parameter +AND setting its display unit to any valid unit of the +target dimension can do the trick

+
+# Let's assume there is an observer called Q_observer in mg/m2 using the dimension Dose per body surface area.
+# Simply using C_max would result in the parameter being shown in umol\l.
+# To see the correct unit and dimension, the following can be done:
+
+QMax <- addUserDefinedPKParameter(
+  name = "Q_max",
+  standardPKParameter = StandardPKParameter$C_max,
+  displayName = "Q max",
+  displayUnit = "mg/m²"
+)
+

What will happen here:

+
    +
  • The dimension of the user defined PK Parameter will be estimated +from the display unit, e.g in this case mg/m² +
  • +
  • The calculated value of the user defined PK Parameter will be +interpreted as value in the base unit of this dimension, e.g. here +“kg/dm²”
  • +
  • If the display unit differs from the base unit of the dimension, +calculated value will be converted into the target display unit
  • +
+

E.g. in the example above:

+
    +
  • First, the C_max value of the quantity will be calculated (because +the user defined parameter is derived from C_max)
  • +
  • From the display unit mg/m², the dimension will be +estimated (“Dose per body surface area”)
  • +
  • The calculated C_max value will be interpreted as a value in +kg/dm² +
  • +
  • Finally, the calculated value will be converted to +mg/m² and set in the user defined PK Parameter.
  • +
+

To see all other options for user defined PK Parameter, refer to the +documentation of UserDefinedPKParameter +(?UserDefinedPKParameter in RStudio)

+
+
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/run-simulation.html b/docs/dev/articles/run-simulation.html new file mode 100644 index 000000000..fc9e16576 --- /dev/null +++ b/docs/dev/articles/run-simulation.html @@ -0,0 +1,382 @@ + + + + + + + + +Running a simulation • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Running individual simulation and retrieving the results +

+

Once the simulation is loaded (see Loading a +simulation and accessing entities), it can be run using +runSimulations() to produce an object of the class +SimulationResults. Keep in mind that +runSimulations() produces a list of +SimulationResults objects.

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Load the simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+simulationResults <- runSimulations(simulations = sim)
+# Extract `SimulationResults` by simulation id
+simulationResults <- simulationResults[[sim$id]]
+print(simulationResults)
+#> SimulationResults: 
+#>    Number of individuals: 1
+

The advantage of storing the results in a object is the option to +keep different results of the same simulation produced with different +settings (e.g., model parameters).

+

Simulated time-value pairs for a specific output from the +SimulationResults-object can be accessed with the method +getOutputValues. The user can provide either the path(s) of +the output (which can be a molecule, a parameter, or an observer), or +the object(s) of the type Molecule, Parameter, +or Quantity (for observers) with the argument +quantitiesOrPaths. If no output is specified, all outputs +available in the simulation results are returned.

+

The paths of all available outputs can be accessed via

+
+simulationResults$allQuantityPaths
+#> [1] "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+

getOutputValues returns a list with two entries: +data and metadata:

+
    +
  • +

    data is a dataframe with two predefined columns +(IndividualId and Time) as well as one column for each requested +output

    +
      +
    • +IndividualId (not relevant for an individual +simulation)
    • +
    • +Time a vector with simulated time values (in minutes, +equal for all outputs)
    • +
    • a vector with simulated entries for each output requested.
    • +
    +
  • +
  • metaData is a list containing one entry for each +requested output. Each entry contains information pertinent to the +output such as its dimension or the unit.

  • +
+
+# Get simulated results by path
+resultsPath <- simulationResults$allQuantityPaths[[1]]
+print(resultsPath)
+#> [1] "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+
+resultsData <- getOutputValues(simulationResults, quantitiesOrPaths = resultsPath)
+
+resultsTime <- resultsData$data$Time
+resultsValues <- resultsData$data$`Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)`
+
+plot(resultsTime, resultsValues, type = "l")
+

+

The results can be stored in and imported from a *.csv file with the +methods exportResultsToCSV and +importResultsFromCSV.

+
+# Load and run the simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+# `runSimulations` returns a list of `SimulationResults`. For single simulation,
+# we directly extract the first element, as only one object is created.
+simulationResults <- runSimulations(simulations = sim)[[1]]
+
+# Export to csv
+csvResultsPath <- system.file("extdata", "SimResults.csv", package = "ospsuite")
+exportResultsToCSV(results = simulationResults, filePath = csvResultsPath)
+
+# Load from csv
+resultsLoaded <- importResultsFromCSV(filePaths = csvResultsPath, simulation = sim)
+print(resultsLoaded)
+#> SimulationResults: 
+#>    Number of individuals: 1
+
+
+

Running multiple individual simulations and retrieving the +results +

+

In some cases, the user might want to run multiple simulations in +parallel. This can be achieved easily by simply passing a list of +simulations to the runSimulations() function. However, only +individual simulations (i.e., the population argument must +remain empty) are supported.

+

By default, the simulations will be executed in parallel by using up +to all cores available on the machine minus 1. So if there are 8 cores +and the user simulates 4 simulations, 4 cores will be used. On the other +hand, if the user simulates 10 simulations with only 8 cores available, +7 will be used and then the first 3 available.

+
+# Load and run multiple simulations concurrently.
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+
+# We load 3 times the same simulation for convenience. But in real life scenarios,
+# they should be different simulations
+sim1 <- loadSimulation(simFilePath)
+sim2 <- loadSimulation(simFilePath)
+sim3 <- loadSimulation(simFilePath)
+
+simulationResults <- runSimulations(simulations = list(sim1, sim2, sim3))
+

The results in this case will be a named list of +SimulationResults-object , i.e. one for each simulation. +The names of the entries of the list are the ids of the simulation to +which the results correspond. This way, it is easy to retrieve the +correct results for the specific simulation

+
+# Get the id of the second simulation
+id <- sim2$id
+print(id)
+#> [1] "ycbdtnPNtECJHtra_yzomQ"
+# get the corresponding result
+sim2Results <- simulationResults[[id]]
+print(sim2Results$simulation$id)
+#> [1] "ycbdtnPNtECJHtra_yzomQ"
+
+
+

Adding new outputs +

+

By default, only outputs that were selected in PK-Sim or MoBi prior +to the export of the simulation to pkml are generated. The +user can add new outputs to the simulation with the method +addOutputs. The outputs can be provided either as objects +of the type(s) Molecule, Parameter, or +Quantity, or as path strings. The output list is a property +of the simulation. After adding or removing outputs, the +corresponding simulation needs to be re-run in order to generate updated +results.

+
+# Clear the list of generated outputs
+clearOutputs(sim)
+
+# Add new outputs as objects
+molecule <- getMolecule("Organism|Kidney|Intracellular|Aciclovir", sim)
+observer <- getQuantity("Organism|Lumen|Aciclovir|Fraction dissolved", sim)
+
+addOutputs(c(molecule, observer), simulation = sim)
+
+# Add new outputs as path strings
+addOutputs(
+  c(
+    "Organism|Lumen|Stomach|Aciclovir",
+    "Organism|PeripheralVenousBlood|Aciclovir|Whole Blood (Peripheral Venous Blood)"
+  ),
+  simulation = sim
+)
+
+# Run simulation
+simulationResults <- runSimulations(simulations = sim)
+
+# Retrieve all generated outputs (e.g. omitting the quantitiesOrPaths property
+# will return all available values)
+resultsData <- getOutputValues(simulationResults)
+
+# Note that "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)"
+# is not in the list of generated results any more
+names(resultsData$data)
+#> NULL
+
+
+

Changing simulation intervals +

+

The simulation interval (i.e., the simulation times at which results +are stored) are stored as the property $outputSchema of a +Simulation object.

+
+print(sim$outputSchema)
+#> OutputSchema: 
+#> Interval: 
+#>    Name: Simulation interval high resolution 
+#>    Start time: 0.00 [min] 
+#>    End time: 15.00 [min] 
+#>    Resolution: 1.00 [pts/min] 
+#> Interval: 
+#>    Name: Simulation Interval 1 
+#>    Start time: 15.00 [min] 
+#>    End time: 1440.00 [min] 
+#>    Resolution: 0.33 [pts/min] 
+#> Interval: 
+#>    Name: Simulation Interval 2 
+#>    Start time: 120.00 [min] 
+#>    End time: 1440.00 [min] 
+#>    Resolution: 0.07 [pts/min]
+

To change the simulation interval, the user can use one of the +functions clearOutputIntervals(), +addOutputInterval(), and +setOutputInterval().

+
+# Remove all output intervals - simulation not possible!
+clearOutputIntervals(simulation = sim)
+runSimulations(simulations = sim)
+#> Error in rClr::clrCall(simulationRunner, "Run", simulationRunArgs): Type:    OSPSuite.Utility.Exceptions.OSPSuiteException
+#> Message: Time points output schema is empty
+#> Method:  Void RunSimulation()
+#> Stack trace:
+#>    at OSPSuite.SimModel.Simulation.RunSimulation()
+#>    at OSPSuite.Core.Domain.Services.SimModelManager.simulate()
+#>    at OSPSuite.Core.Domain.Services.SimModelManager.doIfNotCanceled(Action actionToExecute)
+#>    at OSPSuite.Core.Domain.Services.SimModelManager.RunSimulation(IModelCoreSimulation simulation, SimulationRunOptions simulationRunOptions)
+#>    at OSPSuite.R.Services.SimulationRunner.run(IModelCoreSimulation simulation, SimulationRunOptions simulationRunOptions)
+#>    at OSPSuite.R.Services.SimulationRunner.Run(SimulationRunArgs simulationRunArgs)
+
+# Add an interval
+addOutputInterval(simulation = sim, startTime = 0, endTime = 20, resolution = 60, intervalName = "highRes")
+print(sim$outputSchema)
+#> OutputSchema: 
+#> Interval: 
+#>    Name: highRes 
+#>    Start time: 0.00 [min] 
+#>    End time: 20.00 [min] 
+#>    Resolution: 60.00 [pts/min]
+
+# Add a second interval
+addOutputInterval(simulation = sim, startTime = 30, endTime = 2000, resolution = 4, intervalName = "lowRes")
+print(sim$outputSchema)
+#> OutputSchema: 
+#> Interval: 
+#>    Name: highRes 
+#>    Start time: 0.00 [min] 
+#>    End time: 20.00 [min] 
+#>    Resolution: 60.00 [pts/min] 
+#> Interval: 
+#>    Name: lowRes 
+#>    Start time: 30.00 [min] 
+#>    End time: 2000.00 [min] 
+#>    Resolution: 4.00 [pts/min]
+
+# Replace the existing interval(s) with a new one
+setOutputInterval(simulation = sim, startTime = 0, endTime = 2000, resolution = 4)
+print(sim$outputSchema)
+#> OutputSchema: 
+#> Interval: 
+#>    Name: Output interval 
+#>    Start time: 0.00 [min] 
+#>    End time: 2000.00 [min] 
+#>    Resolution: 4.00 [pts/min]
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/run-simulation_files/figure-html/getOutputValues-1.png b/docs/dev/articles/run-simulation_files/figure-html/getOutputValues-1.png new file mode 100644 index 000000000..2023017cd Binary files /dev/null and b/docs/dev/articles/run-simulation_files/figure-html/getOutputValues-1.png differ diff --git a/docs/dev/articles/sensitivity-analysis.html b/docs/dev/articles/sensitivity-analysis.html new file mode 100644 index 000000000..1ec3636fa --- /dev/null +++ b/docs/dev/articles/sensitivity-analysis.html @@ -0,0 +1,305 @@ + + + + + + + + +Sensitivity analysis • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +

The models built with OSPS depend on a lot of input parameters which +are based on literature values, measurements, databases, and +assumptions. For a given set of input parameters, a number of output +curves is computed in a simulation. To assess which input parameters +have most impact on the output curves, a sensitivity analysis of the +simulation can be performed. For more information about theoretical +background, see OSPS +documentation.

+

In brief, the values of the chosen parameters are changed by a +certain percentage and the impact of these changes on PK parameters of +model outputs is assessed.

+
+

Performing a sensitivity analysis +

+

The first step of performing a sensitivity analysis is creating an +object of the class SensitivityAnalysis. At this step, the +user can define which parameters should be considered for the +sensitivity analysis, in which range the values are varied, and how may +steps are performed in one direction (plus and minus). If no parameters +are specified, all constant and suitable for sensitivity calculations +parameters of the simulation will be varied. The list of such parameters +paths for a simulation can be accessed with the method +potentialVariableParameterPathsFor(simulation).

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Load simulation
+simFilePath <- system.file("extdata", "simple.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Get the paths of parameters that will be considered by default.
+potentialSAParameters <- potentialVariableParameterPathsFor(sim)
+print(potentialSAParameters)
+#> [1] "Organism|Volume" "Organism|Q"      "R1|k1"
+
+# Create a default `SensitivityAnalysis` for the simulation
+sa <- SensitivityAnalysis$new(simulation = sim)
+print(sa)
+#> SensitivityAnalysis: 
+#>    Number of steps: 2 
+#>    Variation range: 0.1 
+#>    Number of parameters to vary: Will be estimated at run time
+
+# Create a `SensitivityAnalysis` with specified parameters
+sa <- SensitivityAnalysis$new(simulation = sim, parameterPaths = c("Organism|Q", "Organism|Volume"))
+print(sa)
+#> SensitivityAnalysis: 
+#>    Number of steps: 2 
+#>    Variation range: 0.1 
+#>    Number of parameters to vary: 2
+
+# Show which parameters will be varied
+sa$parameterPaths
+#> [1] "Organism|Q"      "Organism|Volume"
+

New parameters can be added to an existing +SensitivityAnalysis by calling the method +addParameterPaths().

+

NOTE: - If no parameters were specified during the +creation of a SensitivityAnalysis, all constant and +suitable for sensitivity calculation parameters are considered. - In +such cases, calling addParameterPaths() will only vary the +newly added parameters.

+
+library(ospsuite)
+
+# Load simulation
+simFilePath <- system.file("extdata", "simple.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Create a `SensitivityAnalysis` with specified parameters
+sa <- SensitivityAnalysis$new(simulation = sim, parameterPaths = c(
+  "Organism|Q",
+  "Organism|Volume"
+))
+# Add new parameter
+sa$addParameterPaths("R1|k1")
+
+# Show which parameters will be varied
+sa$parameterPaths
+#> [1] "Organism|Q"      "Organism|Volume" "R1|k1"
+

To run the specified SensitivityAnalysis, call the +method runSensitivityAnalysis(). The method returns an +object of the class SensitivityAnalysisResults. The impact +of the defined parameters is calculated for PK-Parameters (see PK Analysis for more information) of all +model outputs.

+
+# Load simulation
+simFilePath <- system.file("extdata", "simple.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Create a `SensitivityAnalysis` with all constant and suitable for sensitivity calculations parameters and suitable for sensitivity calculations parameters
+sa <- SensitivityAnalysis$new(simulation = sim)
+
+# Run the sensitivity analysis
+saResult <- runSensitivityAnalysis(sa)
+print(saResult)
+#> SensitivityAnalysisResults: 
+#>    Number of calculated sensitivities: 72 
+#>    Available PK parameters: C_max t_max C_tEnd AUC_tEnd Thalf AUC_inf MRT FractionAucLastToInf
+

The method allPKParameterSensitivitiesFor() returns a +list of PKParameterSensitivity objects. +PKParameterSensitivity describes the sensitivity (field +$value) of a PK-Parameter ($pkParameterName) +for the output $outputPath calculated for the varied +parameter $parameterPath. The argument +totalSensitivityThreshold of the method +allPKParameterSensitivitiesFor() is used to filter out the +most impactful parameters. A threshold of 0.9 means that +only parameters participating to a total of 90 percent of the +sensitivity would be returned. A value of 1 would return +the sensitivity for all parameters. If no value is provided, a default +value is used.

+
+# Load simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Default value of the threshold
+getOSPSuiteSetting("sensitivityAnalysisConfig")$totalSensitivityThreshold
+#> [1] 0.9
+
+# Create a `SensitivityAnalysis` with all constant parameters and run it
+sa <- SensitivityAnalysis$new(
+  simulation = sim,
+  parameterPaths = c(
+    "Aciclovir|Lipophilicity",
+    "Aciclovir|Fraction unbound (plasma)",
+    "Organism|Age"
+  )
+)
+saResult <- runSensitivityAnalysis(sa)
+print(saResult)
+#> SensitivityAnalysisResults: 
+#>    Number of calculated sensitivities: 11 
+#>    Available PK parameters: C_max t_max C_tEnd AUC_tEnd AUC_inf MRT Thalf FractionAucLastToInf CL Vss Vd
+
+# Get sensitivities for the parameter "AUC_inf" of the simulated output with a threshold of 0.8
+outputPath <- sim$outputSelections$allOutputs[[1]]$path
+sensitivities <- saResult$allPKParameterSensitivitiesFor(pkParameterName = "AUC_inf", outputPath = outputPath, totalSensitivityThreshold = 0.8)
+print(sensitivities)
+#> [[1]]
+#> PKParameterSensitivity: 
+#>    Parameter name: Aciclovir-Lipophilicity 
+#>    PK-Parameter: AUC_inf 
+#>    Output path: Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood) 
+#>    Value: 0.00470852808265833
+

The value -1 for the sensitivity of “AUC_inf” of the +output +Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood) +for the parameter Organism-Kidney-Volume means that +increasing Organism-Kidney-Volume by 10% will decrease +“AUC_inf” by 10%. Note that the list of sensitivities is ordered from +largest to smallest with respect to magnitude.

+
+
+

Import and export of sensitivity analysis results +

+

Sensitivity analysis calculated in R can be exported to a *.csv file, +which can be loaded in another instance.

+
+# Load and run the simulation
+simFilePath <- system.file("extdata", "simple.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+simulationResults <- runSimulation(simulation = sim)
+
+# Create a `SensitivityAnalysis` with all constant parameters and run it
+sa <- SensitivityAnalysis$new(simulation = sim)
+saResult <- runSensitivityAnalysis(sa)
+
+# Export to csv
+saResultPath <- system.file("extdata", "SAResult.csv", package = "ospsuite")
+exportSensitivityAnalysisResultsToCSV(results = saResult, filePath = saResultPath)
+
+# Load from csv
+saResultLoaded <- importSensitivityAnalysisResultsFromCSV(filePaths = saResultPath, simulation = sim)
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/set-values.html b/docs/dev/articles/set-values.html new file mode 100644 index 000000000..036195ae0 --- /dev/null +++ b/docs/dev/articles/set-values.html @@ -0,0 +1,411 @@ + + + + + + + + +Changing parameter and molecule start values • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Changing initial values +

+

It is possible to change the initial values of all parameters and +molecules in a simulation. It is important to differentiate between the +constant values and the values that are defined by a +formula.

+
+

Formula types +

+

Every (initial) value is described either by a constant or by a +formula. If the value is defined by a simple constant, the field +isConstant of the respective parameter or molecule has the +value TRUE.

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Load simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Get the parameter "Age" of the Organism
+ageParam <- getParameter("Organism|Age", sim)
+print(ageParam)
+#> Parameter: 
+#>    Path: Organism|Age 
+#>    Value: 25.70 [year(s)] 
+#>    isConstant: TRUE 
+#>    isStateVariable: FALSE
+ageParam$isConstant
+#> [1] TRUE
+

If the value is not constant, it is described by one of the formula +types:

+

Distributed: Distributed parameters describe a +variation around a constant value or between two numerical limits. In +the simulation, the behavior equals that of constant values. See OSPS +documentation for more information.

+
+# Get the parameter "Volume" of the Liver
+liverVolume <- getParameter("Organism|Liver|Volume", sim)
+print(liverVolume)
+#> Parameter: 
+#>    Path: Organism|Liver|Volume 
+#>    Value: 2.17 [l] 
+#>    isDistributed: TRUE 
+#>    isStateVariable: FALSE
+liverVolume$isDistributed
+#> [1] TRUE
+

Explicit formula: The value of the parameter is +given by an explicit formula. The value of an explicit formula can +change during the simulation. The string of the formula can be accessed +via parameter$formulaString.

+
+# Get the parameter "Volume" of the Liver interstital
+liverIntVolume <- getParameter("Organism|Liver|Interstitial|Volume", sim)
+print(liverIntVolume)
+#> Parameter: 
+#>    Path: Organism|Liver|Interstitial|Volume 
+#>    Value: 0.35 [l] 
+#>    isFormula: TRUE 
+#>    formula: f_int*V 
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+liverIntVolume$formulaString
+#> [1] "f_int*V"
+

Table formula: The value of the parameter is given +by a table with x-y value pairs. X values usually refer to the +simulation time. See Table +parameters for additional information on how to retrieve or change +the table values.

+
+# Get the parameter defined by a table.
+tableParam <- getParameter("Organism|TableParameter", sim)
+print(tableParam)
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 0.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+

Additionally, some parameters are modeled as state +variables. In this case, the parameter has an initial +value and a right hand side (RHS) formula, both of which +can be any formula type.

+
+# Get the parameter defined by a state variable.
+stateVariableParam <- getParameter("Organism|StateVariable_Parameter", sim)
+print(stateVariableParam)
+#> Parameter: 
+#>    Path: Organism|StateVariable_Parameter 
+#>    Value: 0.00 
+#>    isConstant: TRUE 
+#>    isStateVariable: TRUE 
+#>    RHSFormula: 
+#>    isFormula: TRUE 
+#>    formula: Time * 2
+
+# `value` refers to the initial value of the parameter
+stateVariableParam$value
+#> [1] 0
+# `rhsFormula` is the right hand side of the parameter
+stateVariableParam$rhsFormula
+#> Formula: 
+#>    isFormula: TRUE 
+#>    formula: Time * 2
+
+
+

Changing parameters and molecules initial values +

+

The user can change initial values of parameters and molecules with +the methods setParameterValues and +setMoleculeInitialValues, respectively.

+
+# Get the parameter Dose
+doseParamPath <- "Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose"
+doseParam <- getParameter(doseParamPath, sim)
+print(doseParam)
+#> Parameter: 
+#>    Path: Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose 
+#>    Value: 0.00 [kg] 
+#>    isConstant: TRUE 
+#>    isStateVariable: FALSE
+
+# Change the dose to 350mg. The value has to be converted to base unit, first
+newValue <- toBaseUnit(quantity = doseParam, values = 350, unit = "mg")
+setParameterValues(parameters = doseParam, values = newValue)
+print(doseParam)
+#> Parameter: 
+#>    Path: Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose 
+#>    Value: 0.00 [kg] 
+#>    isConstant: TRUE 
+#>    isStateVariable: FALSE
+

Another way to change parameter values is to scale them. The scaling +is always performed relative to the current value:

+
+doseParamPath <- "Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose"
+doseParam <- getParameter(doseParamPath, sim)
+print(doseParam)
+#> Parameter: 
+#>    Path: Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose 
+#>    Value: 0.00 [kg] 
+#>    isConstant: TRUE 
+#>    isStateVariable: FALSE
+
+# Double the dose
+scaleParameterValues(doseParam, factor = 2)
+print(doseParam)
+#> Parameter: 
+#>    Path: Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose 
+#>    Value: 0.00 [kg] 
+#>    isConstant: TRUE 
+#>    isStateVariable: FALSE
+
+# Half the dose
+scaleParameterValues(doseParam, factor = 0.5)
+print(doseParam)
+#> Parameter: 
+#>    Path: Applications|IV 250mg 10min|Application_1|ProtocolSchemaItem|Dose 
+#>    Value: 0.00 [kg] 
+#>    isConstant: TRUE 
+#>    isStateVariable: FALSE
+

Only constant values can be set. If the parameters value is defined +by a formula (explicit or table), the newly assigned value will override +the formula. This will be reflected by the field +isFixedValue. Changing values of formula parameters should +be done with caution, as the potential dependency on another simulation +parameters will be destroyed.

+
+# Get the parameter "Volume" of the Liver interstital
+liverIntVolume <- getParameter("Organism|Liver|Interstitial|Volume", sim)
+print(liverIntVolume)
+#> Parameter: 
+#>    Path: Organism|Liver|Interstitial|Volume 
+#>    Value: 0.35 [l] 
+#>    isFormula: TRUE 
+#>    formula: f_int*V 
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+
+# isFixedValue is FALSE as the parameter is defined by its formula
+liverIntVolume$isFixedValue
+#> [1] FALSE
+
+setParameterValues(liverIntVolume, 1)
+print(liverIntVolume)
+#> Parameter: 
+#>    Path: Organism|Liver|Interstitial|Volume 
+#>    Value: 1.00 [l] 
+#>    isFormula: TRUE 
+#>    formula: f_int*V 
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+# isFixedValue is TRUE as the value of parameter is overridden by the constant value
+liverIntVolume$isFixedValue
+#> [1] TRUE
+

The parameter value can be reset to its formula after assigning a +constant value:

+
+print(liverIntVolume)
+#> Parameter: 
+#>    Path: Organism|Liver|Interstitial|Volume 
+#>    Value: 1.00 [l] 
+#>    isFormula: TRUE 
+#>    formula: f_int*V 
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+# isFixedValue is TRUE as the value of parameter is overridden by the constant value
+liverIntVolume$isFixedValue
+#> [1] TRUE
+
+liverIntVolume$reset()
+print(liverIntVolume)
+#> Parameter: 
+#>    Path: Organism|Liver|Interstitial|Volume 
+#>    Value: 0.35 [l] 
+#>    isFormula: TRUE 
+#>    formula: f_int*V 
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+liverIntVolume$isFixedValue
+#> [1] FALSE
+

Changing the value of a state variable parameter will only change its +initial value, but not the right hand side (i.e., the value in the +simulation will still change according to the RHS formula). To switch to +RHS formula off and, by that, make the parameter be only dependent on +its initial value, the field isStateVariable can be set to +FALSE. This function is one-way only! It is not possible to +re-activate the RHS formula after switching it off.

+
+# Get the parameter defined by a state variable.
+stateVariableParam <- getParameter("Organism|StateVariable_Parameter", sim)
+print(stateVariableParam)
+#> Parameter: 
+#>    Path: Organism|StateVariable_Parameter 
+#>    Value: 0.00 
+#>    isConstant: TRUE 
+#>    isStateVariable: TRUE 
+#>    RHSFormula: 
+#>    isFormula: TRUE 
+#>    formula: Time * 2
+
+# Setting its value only changes the initial value
+setParameterValues(stateVariableParam, 10)
+print(stateVariableParam)
+#> Parameter: 
+#>    Path: Organism|StateVariable_Parameter 
+#>    Value: 10.00 
+#>    isConstant: TRUE 
+#>    isStateVariable: TRUE 
+#>    RHSFormula: 
+#>    isFormula: TRUE 
+#>    formula: Time * 2
+
+# Switching the RHS formula off
+stateVariableParam$isStateVariable <- FALSE
+print(stateVariableParam)
+#> Parameter: 
+#>    Path: Organism|StateVariable_Parameter 
+#>    Value: 10.00 
+#>    isConstant: TRUE 
+#>    isStateVariable: FALSE
+
+# Switching it on is not supported
+stateVariableParam$isStateVariable <- TRUE
+#> Error in (function (value) : Creating a RHS Formula is not supported at the moment. This should be done in MoBi.
+

An example how to set the initial values of molecules in all +containers to a certain value:

+
+# Get objects representing the molecule Aciclovir in all containers
+allAciclovirMolecules <- getAllMoleculesMatching("Organism|**|Aciclovir", sim)
+
+# Set initial values to 10 µmol in all containers
+setMoleculeInitialValues(allAciclovirMolecules, rep(10, length(allAciclovirMolecules)))
+
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/table-parameters.html b/docs/dev/articles/table-parameters.html new file mode 100644 index 000000000..6138dd0cb --- /dev/null +++ b/docs/dev/articles/table-parameters.html @@ -0,0 +1,446 @@ + + + + + + + + +Table parameters • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +

Parameters defined by a table formula have special +syntax for retrieving and changing the values. The value of a table +parameter is given by a list of ValuePoint objects, each +ValuePoint being an x-y value pair. X values usually refer +to the simulation time.

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Load a simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Get the parameter defined by a table.
+tableParam <- getParameter("Organism|TableParameter", sim)
+print(tableParam)
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 0.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+

Direct access to the value points is possible through the +TableFormula of the table parameter. All x- or y-values +stored in the table can be conveniently retrieved using the +lapply method:

+
+# Get the parameter defined by a table
+tableParam <- getParameter("Organism|TableParameter", sim)
+print(tableParam)
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 0.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+
+# Get all value points
+tableParam$formula$allPoints
+#> [[1]]
+#>   x= 0, y= 0, restartSolver= FALSE
+#> 
+#> [[2]]
+#>   x= 60, y= 1, restartSolver= FALSE
+#> 
+#> [[3]]
+#>   x= 120, y= 2, restartSolver= FALSE
+#> 
+#> [[4]]
+#>   x= 180, y= 3, restartSolver= FALSE
+
+# Get all x-values
+xValues <- lapply(tableParam$formula$allPoints, function(point) {
+  point$x
+})
+print(xValues)
+#> [[1]]
+#> [1] 0
+#> 
+#> [[2]]
+#> [1] 60
+#> 
+#> [[3]]
+#> [1] 120
+#> 
+#> [[4]]
+#> [1] 180
+
+# Get all y-values
+yValues <- lapply(tableParam$formula$allPoints, function(point) {
+  point$y
+})
+print(yValues)
+#> [[1]]
+#> [1] 0
+#> 
+#> [[2]]
+#> [1] 1
+#> 
+#> [[3]]
+#> [1] 2
+#> 
+#> [[4]]
+#> [1] 3
+

The method valueAt() of the TableFormula +returns the value of y for the given x. If no +entry exists for the x, the value y is +linearly interpolated between the two closest x values.

+
+# Get the parameter defined by a table
+tableParam <- getParameter("Organism|TableParameter", sim)
+
+# Value at x = 60 is stored in the table
+tableParam$formula$valueAt(60)
+#> [1] 1
+
+# Value at x = 90 is not in the table
+tableParam$formula$valueAt(90)
+#> [1] 1.5
+
+

Changing table parameter values +

+

Simply setting the value of a table-defined parameter using +setParameterValues will override the formula and make the +parameter constant.

+
+
+# Get the parameter defined by a table.
+tableParam <- getParameter("Organism|TableParameter", sim)
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 0.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+
+# Set value to a constant. tableParam$isFixedValue is now TRUE
+setParameterValues(tableParam, 10)
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+

To change the values of the table, a set of methods of the +TableFormula is available. The method +addPoints() adds a set of x-y values to the existing table. +If trying to add a point with the x-value already present in the table, +an error is thrown:

+
+tableParam <- getParameter("Organism|TableParameter", sim)
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+# Add new points
+tableParam$formula$addPoints(c(1, 2, 3), c(5, 6, 7))
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 1, y= 5, restartSolver= FALSE
+#>   x= 2, y= 6, restartSolver= FALSE
+#>   x= 3, y= 7, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+# Try to add points with existing x-values
+tableParam$formula$addPoints(0, 1)
+#> Error in rClr::clrCall(self$ref, "AddPoint", xValues[i], yValues[i]): Type:    OSPSuite.Core.Domain.ValuePointAlreadyExistsForPointException
+#> Message: A point for x=0 was already added with y=1
+#> Method:  Int32 AddPoint(OSPSuite.Core.Domain.Formulas.ValuePoint)
+#> Stack trace:
+#>    at OSPSuite.Core.Domain.Formulas.TableFormula.AddPoint(ValuePoint point)
+#>    at OSPSuite.Core.Domain.Formulas.TableFormula.AddPoint(Double x, Double y)
+

To remove a point from the table, use the method +removePoint(). It remove a point if the x value is present +in the table and has the provided y.

+
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 0, y= 0, restartSolver= FALSE
+#>   x= 1, y= 5, restartSolver= FALSE
+#>   x= 2, y= 6, restartSolver= FALSE
+#>   x= 3, y= 7, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+# Remove the point (0, 0)
+tableParam$formula$removePoint(0, 0)
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 1, y= 5, restartSolver= FALSE
+#>   x= 2, y= 6, restartSolver= FALSE
+#>   x= 3, y= 7, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+# Try to remove the point (1, 1). Note that the value for x = 1 is x = 5 in the original table,
+# and no point is removed.
+tableParam$formula$removePoint(1, 1)
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 1, y= 5, restartSolver= FALSE
+#>   x= 2, y= 6, restartSolver= FALSE
+#>   x= 3, y= 7, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+# Try to remove a non-existing point (0, 1). No point is removed.
+tableParam$formula$removePoint(1, 1)
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 1, y= 5, restartSolver= FALSE
+#>   x= 2, y= 6, restartSolver= FALSE
+#>   x= 3, y= 7, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+

The clearPoints() method removes all points from the +table, while setPoints() method is a combination of +clearing the table and adding new points:

+
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 1, y= 5, restartSolver= FALSE
+#>   x= 2, y= 6, restartSolver= FALSE
+#>   x= 3, y= 7, restartSolver= FALSE
+#>   x= 60, y= 1, restartSolver= FALSE
+#>   x= 120, y= 2, restartSolver= FALSE
+#>   x= 180, y= 3, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+tableParam$formula$setPoints(c(1, 2, 3, 4), c(5, 6, 7, 8))
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>   x= 1, y= 5, restartSolver= FALSE
+#>   x= 2, y= 6, restartSolver= FALSE
+#>   x= 3, y= 7, restartSolver= FALSE
+#>   x= 4, y= 8, restartSolver= FALSE
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+tableParam$formula$clearPoints()
+tableParam
+#> Parameter: 
+#>    Path: Organism|TableParameter 
+#>    Value: 10.00 
+#>    isTable: TRUE 
+#>    XDimension: Time 
+#>    UseDerivedValues: FALSE 
+#>    Value overrides formula: TRUE 
+#>    isStateVariable: FALSE
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/articles/unit-conversion.html b/docs/dev/articles/unit-conversion.html new file mode 100644 index 000000000..f75562ab2 --- /dev/null +++ b/docs/dev/articles/unit-conversion.html @@ -0,0 +1,220 @@ + + + + + + + + +Dimensions and Units • ospsuite + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +
+

Unit conversion +

+

Every entity - a molecule, a parameter, or an observer - has a +certain dimension, like Amount, Concentration, or +Volume. The dimension is a property of an entity:

+
+library(ospsuite)
+#> Loading required package: rClr
+#> Loading the dynamic library for Microsoft .NET runtime...
+#> Loaded Common Language Runtime version 4.0.30319.42000
+
+# Load a simulation
+simFilePath <- system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
+sim <- loadSimulation(simFilePath)
+
+# Get the parameter volume of the liver.
+livParam <- getParameter("Organism|Liver|Volume", sim)
+print(livParam)
+#> Parameter: 
+#>    Path: Organism|Liver|Volume 
+#>    Value: 2.17 [l] 
+#>    isDistributed: TRUE 
+#>    isStateVariable: FALSE
+
+# Dimension of the parameter
+livParam$dimension
+#> [1] "Volume"
+

The values of a certain dimension can be presented in different units +- for example, l or ml for the dimension +Volume, or mol and µmol for the dimension +Amount. The list of all available units for an entity can be +obtained using allUnits() method:

+
+# Dimension of the parameter
+livParam$dimension
+#> [1] "Volume"
+
+# Units of the parameter
+livParam$allUnits
+#> [1] "l"  "ml" "µl"
+

Internally, OSPS works with the base +units, and all the values that are shown or passed to functions +are in base units by default. These base units are often different from +the units that are displayed by default in PK-Sim (and MoBi). The list +of base and default display units can be found in the documentation.

+

As an example, the parameter BMI is given in the +default unit kg/dm², while the default display unit is the +more convenient kg/m².

+

The {ospuite} package provides a set of methods for +conversion between different units. The methods toUnit(), +toBaseUnit(), and toDisplayUnit() require the +quantity to get the correct dimension and units; however, it does not +change the value of the quantity!

+
+# Get the BMI parameter
+bmiParam <- getParameter("Organism|BMI", sim)
+print(bmiParam)
+#> Parameter: 
+#>    Path: Organism|BMI 
+#>    Value: 0.23 [kg/dm²] 
+#>    isFormula: TRUE 
+#>    formula: Height>0 ? BW / (Height)^2 : 0 
+#>    Value overrides formula: FALSE 
+#>    isStateVariable: FALSE
+
+# Print the base and the default display units
+bmiParam$unit
+#> [1] "kg/dm²"
+bmiParam$displayUnit
+#> [1] "kg/m²"
+
+# Convert the value from the base into the default display unit
+toDisplayUnit(quantity = bmiParam, values = bmiParam$value)
+#> [1] 23.43633
+
+# Convert the value to the base unit, that can be used. e.g. for setting new parameter value
+toBaseUnit(quantity = bmiParam, values = 30, unit = "kg/m²")
+#> [1] 0.3
+
+liverVolume <- getParameter("Organism|Liver|Volume", sim)
+print(liverVolume)
+#> Parameter: 
+#>    Path: Organism|Liver|Volume 
+#>    Value: 2.17 [l] 
+#>    isDistributed: TRUE 
+#>    isStateVariable: FALSE
+
+liverVolume$allUnits
+#> [1] "l"  "ml" "µl"
+
+# Convert from base volume unit to µl
+toUnit(quantity = liverVolume, values = c(1, 2, 3, 4), targetUnit = "ml")
+#> [1] 1000 2000 3000 4000
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/dev/authors.html b/docs/dev/authors.html new file mode 100644 index 000000000..b47a68cda --- /dev/null +++ b/docs/dev/authors.html @@ -0,0 +1,133 @@ + +Authors and Citation • ospsuite + Skip to contents + + +
+
+
+ +
+

Authors

+ +
  • +

    Open-Systems-Pharmacology Community. Copyright holder. +

    +
  • +
  • +

    Michael Sevestre. Author, maintainer. +

    +
  • +
  • +

    Pavel Balazki. Author. +

    +
  • +
  • +

    Juri Solodenko. Author. +

    +
  • +
  • +

    Indrajeet Patil. Author. +
    @patilindrajeets

    +
  • +
+ +
+

Citation

+

Source: DESCRIPTION

+ +

Sevestre M, Balazki P, Solodenko J, Patil I (2022). +ospsuite: R package to manipulate OSPSuite Models. +https://github.com/open-systems-pharmacology/ospsuite-r, +https://www.open-systems-pharmacology.org/OSPSuite-R/ (release), +https://www.open-systems-pharmacology.org/OSPSuite-R/dev (development). +

+
@Manual{,
+  title = {ospsuite: R package to manipulate OSPSuite Models},
+  author = {Michael Sevestre and Pavel Balazki and Juri Solodenko and Indrajeet Patil},
+  year = {2022},
+  note = {https://github.com/open-systems-pharmacology/ospsuite-r,
+https://www.open-systems-pharmacology.org/OSPSuite-R/ (release),
+https://www.open-systems-pharmacology.org/OSPSuite-R/dev (development)},
+}
+
+
+ + +
+ + + + + + + diff --git a/docs/dev/deps/bootstrap-5.1.3/bootstrap.bundle.min.js b/docs/dev/deps/bootstrap-5.1.3/bootstrap.bundle.min.js new file mode 100644 index 000000000..cc0a25561 --- /dev/null +++ b/docs/dev/deps/bootstrap-5.1.3/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.1.3 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",(()=>{p.forEach((t=>t()))})),p.push(e)):e()},_=t=>{"function"==typeof t&&t()},b=(e,i,n=!0)=>{if(!n)return void _(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(i)+5;let r=!1;const a=({target:n})=>{n===i&&(r=!0,i.removeEventListener(t,a),_(e))};i.addEventListener(t,a),setTimeout((()=>{r||s(i)}),o)},v=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,E=/::\d+$/,A={};let T=1;const O={mouseenter:"mouseover",mouseleave:"mouseout"},C=/^(mouseenter|mouseleave)/i,k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function x(t){const e=L(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function D(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;sfunction(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=S(e,i,n),l=x(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=L(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&j.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&j.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function I(t,e,i,n,s){const o=D(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function P(t){return t=t.replace(w,""),O[t]||t}const j={on(t,e,i,n){N(t,e,i,n,!1)},one(t,e,i,n){N(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=x(t),c=e.startsWith(".");if(void 0!==o){if(!l||!l[r])return;return void I(t,l,r,o,s?i:null)}c&&Object.keys(l).forEach((i=>{!function(t,e,i,n){const s=e[i]||{};Object.keys(s).forEach((o=>{if(o.includes(n)){const n=s[o];I(t,e,i,n.originalHandler,n.delegationSelector)}}))}(t,l,i,e.slice(1))}));const h=l[r]||{};Object.keys(h).forEach((i=>{const n=i.replace(E,"");if(!a||e.includes(n)){const e=h[i];I(t,l,r,e.originalHandler,e.delegationSelector)}}))},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=f(),s=P(e),o=e!==s,r=k.has(s);let a,l=!0,c=!0,h=!1,d=null;return o&&n&&(a=n.Event(e,i),n(t).trigger(a),l=!a.isPropagationStopped(),c=!a.isImmediatePropagationStopped(),h=a.isDefaultPrevented()),r?(d=document.createEvent("HTMLEvents"),d.initEvent(s,l,!0)):d=new CustomEvent(e,{bubbles:l,cancelable:!0}),void 0!==i&&Object.keys(i).forEach((t=>{Object.defineProperty(d,t,{get:()=>i[t]})})),h&&d.preventDefault(),c&&t.dispatchEvent(d),d.defaultPrevented&&void 0!==a&&a.preventDefault(),d}},M=new Map,H={set(t,e,i){M.has(t)||M.set(t,new Map);const n=M.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>M.has(t)&&M.get(t).get(e)||null,remove(t,e){if(!M.has(t))return;const i=M.get(t);i.delete(e),0===i.size&&M.delete(t)}};class B{constructor(t){(t=r(t))&&(this._element=t,H.set(this._element,this.constructor.DATA_KEY,this))}dispose(){H.remove(this._element,this.constructor.DATA_KEY),j.off(this._element,this.constructor.EVENT_KEY),Object.getOwnPropertyNames(this).forEach((t=>{this[t]=null}))}_queueCallback(t,e,i=!0){b(t,e,i)}static getInstance(t){return H.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.1.3"}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}}const R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,s=t.NAME;j.on(document,i,`[data-bs-dismiss="${s}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),c(this))return;const o=n(this)||this.closest(`.${s}`);t.getOrCreateInstance(o)[e]()}))};class W extends B{static get NAME(){return"alert"}close(){if(j.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),j.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=W.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(W,"close"),g(W);const $='[data-bs-toggle="button"]';class z extends B{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=z.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}function q(t){return"true"===t||"false"!==t&&(t===Number(t).toString()?Number(t):""===t||"null"===t?null:t)}function F(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}j.on(document,"click.bs.button.data-api",$,(t=>{t.preventDefault();const e=t.target.closest($);z.getOrCreateInstance(e).toggle()})),g(z);const U={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${F(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${F(e)}`)},getDataAttributes(t){if(!t)return{};const e={};return Object.keys(t.dataset).filter((t=>t.startsWith("bs"))).forEach((i=>{let n=i.replace(/^bs/,"");n=n.charAt(0).toLowerCase()+n.slice(1,n.length),e[n]=q(t.dataset[i])})),e},getDataAttribute:(t,e)=>q(t.getAttribute(`data-bs-${F(e)}`)),offset(t){const e=t.getBoundingClientRect();return{top:e.top+window.pageYOffset,left:e.left+window.pageXOffset}},position:t=>({top:t.offsetTop,left:t.offsetLeft})},V={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode;for(;n&&n.nodeType===Node.ELEMENT_NODE&&3!==n.nodeType;)n.matches(e)&&i.push(n),n=n.parentNode;return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(", ");return this.find(e,t).filter((t=>!c(t)&&l(t)))}},K="carousel",X={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},Y={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},Q="next",G="prev",Z="left",J="right",tt={ArrowLeft:J,ArrowRight:Z},et="slid.bs.carousel",it="active",nt=".active.carousel-item";class st extends B{constructor(t,e){super(t),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._indicatorsElement=V.findOne(".carousel-indicators",this._element),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent),this._addEventListeners()}static get Default(){return X}static get NAME(){return K}next(){this._slide(Q)}nextWhenVisible(){!document.hidden&&l(this._element)&&this.next()}prev(){this._slide(G)}pause(t){t||(this._isPaused=!0),V.findOne(".carousel-item-next, .carousel-item-prev",this._element)&&(s(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null}cycle(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))}to(t){this._activeElement=V.findOne(nt,this._element);const e=this._getItemIndex(this._activeElement);if(t>this._items.length-1||t<0)return;if(this._isSliding)return void j.one(this._element,et,(()=>this.to(t)));if(e===t)return this.pause(),void this.cycle();const i=t>e?Q:G;this._slide(i,this._items[t])}_getConfig(t){return t={...X,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(K,t,Y),t}_handleSwipe(){const t=Math.abs(this.touchDeltaX);if(t<=40)return;const e=t/this.touchDeltaX;this.touchDeltaX=0,e&&this._slide(e>0?J:Z)}_addEventListeners(){this._config.keyboard&&j.on(this._element,"keydown.bs.carousel",(t=>this._keydown(t))),"hover"===this._config.pause&&(j.on(this._element,"mouseenter.bs.carousel",(t=>this.pause(t))),j.on(this._element,"mouseleave.bs.carousel",(t=>this.cycle(t)))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners()}_addTouchEventListeners(){const t=t=>this._pointerEvent&&("pen"===t.pointerType||"touch"===t.pointerType),e=e=>{t(e)?this.touchStartX=e.clientX:this._pointerEvent||(this.touchStartX=e.touches[0].clientX)},i=t=>{this.touchDeltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this.touchStartX},n=e=>{t(e)&&(this.touchDeltaX=e.clientX-this.touchStartX),this._handleSwipe(),"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((t=>this.cycle(t)),500+this._config.interval))};V.find(".carousel-item img",this._element).forEach((t=>{j.on(t,"dragstart.bs.carousel",(t=>t.preventDefault()))})),this._pointerEvent?(j.on(this._element,"pointerdown.bs.carousel",(t=>e(t))),j.on(this._element,"pointerup.bs.carousel",(t=>n(t))),this._element.classList.add("pointer-event")):(j.on(this._element,"touchstart.bs.carousel",(t=>e(t))),j.on(this._element,"touchmove.bs.carousel",(t=>i(t))),j.on(this._element,"touchend.bs.carousel",(t=>n(t))))}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=tt[t.key];e&&(t.preventDefault(),this._slide(e))}_getItemIndex(t){return this._items=t&&t.parentNode?V.find(".carousel-item",t.parentNode):[],this._items.indexOf(t)}_getItemByOrder(t,e){const i=t===Q;return v(this._items,e,i,this._config.wrap)}_triggerSlideEvent(t,e){const i=this._getItemIndex(t),n=this._getItemIndex(V.findOne(nt,this._element));return j.trigger(this._element,"slide.bs.carousel",{relatedTarget:t,direction:e,from:n,to:i})}_setActiveIndicatorElement(t){if(this._indicatorsElement){const e=V.findOne(".active",this._indicatorsElement);e.classList.remove(it),e.removeAttribute("aria-current");const i=V.find("[data-bs-target]",this._indicatorsElement);for(let e=0;e{j.trigger(this._element,et,{relatedTarget:o,direction:d,from:s,to:r})};if(this._element.classList.contains("slide")){o.classList.add(h),u(o),n.classList.add(c),o.classList.add(c);const t=()=>{o.classList.remove(c,h),o.classList.add(it),n.classList.remove(it,h,c),this._isSliding=!1,setTimeout(f,0)};this._queueCallback(t,n,!0)}else n.classList.remove(it),o.classList.add(it),this._isSliding=!1,f();a&&this.cycle()}_directionToOrder(t){return[J,Z].includes(t)?m()?t===Z?G:Q:t===Z?Q:G:t}_orderToDirection(t){return[Q,G].includes(t)?m()?t===G?Z:J:t===G?J:Z:t}static carouselInterface(t,e){const i=st.getOrCreateInstance(t,e);let{_config:n}=i;"object"==typeof e&&(n={...n,...e});const s="string"==typeof e?e:n.slide;if("number"==typeof e)i.to(e);else if("string"==typeof s){if(void 0===i[s])throw new TypeError(`No method named "${s}"`);i[s]()}else n.interval&&n.ride&&(i.pause(),i.cycle())}static jQueryInterface(t){return this.each((function(){st.carouselInterface(this,t)}))}static dataApiClickHandler(t){const e=n(this);if(!e||!e.classList.contains("carousel"))return;const i={...U.getDataAttributes(e),...U.getDataAttributes(this)},s=this.getAttribute("data-bs-slide-to");s&&(i.interval=!1),st.carouselInterface(e,i),s&&st.getInstance(e).to(s),t.preventDefault()}}j.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",st.dataApiClickHandler),j.on(window,"load.bs.carousel.data-api",(()=>{const t=V.find('[data-bs-ride="carousel"]');for(let e=0,i=t.length;et===this._element));null!==s&&o.length&&(this._selector=s,this._triggerArray.push(e))}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return rt}static get NAME(){return ot}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t,e=[];if(this._config.parent){const t=V.find(ut,this._config.parent);e=V.find(".collapse.show, .collapse.collapsing",this._config.parent).filter((e=>!t.includes(e)))}const i=V.findOne(this._selector);if(e.length){const n=e.find((t=>i!==t));if(t=n?pt.getInstance(n):null,t&&t._isTransitioning)return}if(j.trigger(this._element,"show.bs.collapse").defaultPrevented)return;e.forEach((e=>{i!==e&&pt.getOrCreateInstance(e,{toggle:!1}).hide(),t||H.set(e,"bs.collapse",null)}));const n=this._getDimension();this._element.classList.remove(ct),this._element.classList.add(ht),this._element.style[n]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const s=`scroll${n[0].toUpperCase()+n.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct,lt),this._element.style[n]="",j.trigger(this._element,"shown.bs.collapse")}),this._element,!0),this._element.style[n]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(j.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,u(this._element),this._element.classList.add(ht),this._element.classList.remove(ct,lt);const e=this._triggerArray.length;for(let t=0;t{this._isTransitioning=!1,this._element.classList.remove(ht),this._element.classList.add(ct),j.trigger(this._element,"hidden.bs.collapse")}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(lt)}_getConfig(t){return(t={...rt,...U.getDataAttributes(this._element),...t}).toggle=Boolean(t.toggle),t.parent=r(t.parent),a(ot,t,at),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=V.find(ut,this._config.parent);V.find(ft,this._config.parent).filter((e=>!t.includes(e))).forEach((t=>{const e=n(t);e&&this._addAriaAndCollapsedClass([t],this._isShown(e))}))}_addAriaAndCollapsedClass(t,e){t.length&&t.forEach((t=>{e?t.classList.remove(dt):t.classList.add(dt),t.setAttribute("aria-expanded",e)}))}static jQueryInterface(t){return this.each((function(){const e={};"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1);const i=pt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}j.on(document,"click.bs.collapse.data-api",ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();const e=i(this);V.find(e).forEach((t=>{pt.getOrCreateInstance(t,{toggle:!1}).toggle()}))})),g(pt);var mt="top",gt="bottom",_t="right",bt="left",vt="auto",yt=[mt,gt,_t,bt],wt="start",Et="end",At="clippingParents",Tt="viewport",Ot="popper",Ct="reference",kt=yt.reduce((function(t,e){return t.concat([e+"-"+wt,e+"-"+Et])}),[]),Lt=[].concat(yt,[vt]).reduce((function(t,e){return t.concat([e,e+"-"+wt,e+"-"+Et])}),[]),xt="beforeRead",Dt="read",St="afterRead",Nt="beforeMain",It="main",Pt="afterMain",jt="beforeWrite",Mt="write",Ht="afterWrite",Bt=[xt,Dt,St,Nt,It,Pt,jt,Mt,Ht];function Rt(t){return t?(t.nodeName||"").toLowerCase():null}function Wt(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function $t(t){return t instanceof Wt(t).Element||t instanceof Element}function zt(t){return t instanceof Wt(t).HTMLElement||t instanceof HTMLElement}function qt(t){return"undefined"!=typeof ShadowRoot&&(t instanceof Wt(t).ShadowRoot||t instanceof ShadowRoot)}const Ft={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];zt(s)&&Rt(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});zt(n)&&Rt(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function Ut(t){return t.split("-")[0]}function Vt(t,e){var i=t.getBoundingClientRect();return{width:i.width/1,height:i.height/1,top:i.top/1,right:i.right/1,bottom:i.bottom/1,left:i.left/1,x:i.left/1,y:i.top/1}}function Kt(t){var e=Vt(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Xt(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&qt(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Yt(t){return Wt(t).getComputedStyle(t)}function Qt(t){return["table","td","th"].indexOf(Rt(t))>=0}function Gt(t){return(($t(t)?t.ownerDocument:t.document)||window.document).documentElement}function Zt(t){return"html"===Rt(t)?t:t.assignedSlot||t.parentNode||(qt(t)?t.host:null)||Gt(t)}function Jt(t){return zt(t)&&"fixed"!==Yt(t).position?t.offsetParent:null}function te(t){for(var e=Wt(t),i=Jt(t);i&&Qt(i)&&"static"===Yt(i).position;)i=Jt(i);return i&&("html"===Rt(i)||"body"===Rt(i)&&"static"===Yt(i).position)?e:i||function(t){var e=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&zt(t)&&"fixed"===Yt(t).position)return null;for(var i=Zt(t);zt(i)&&["html","body"].indexOf(Rt(i))<0;){var n=Yt(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function ee(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}var ie=Math.max,ne=Math.min,se=Math.round;function oe(t,e,i){return ie(t,ne(e,i))}function re(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function ae(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const le={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=Ut(i.placement),l=ee(a),c=[bt,_t].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return re("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:ae(t,yt))}(s.padding,i),d=Kt(o),u="y"===l?mt:bt,f="y"===l?gt:_t,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=te(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,E=oe(v,w,y),A=l;i.modifiersData[n]=((e={})[A]=E,e.centerOffset=E-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Xt(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ce(t){return t.split("-")[1]}var he={top:"auto",right:"auto",bottom:"auto",left:"auto"};function de(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=!0===h?function(t){var e=t.x,i=t.y,n=window.devicePixelRatio||1;return{x:se(se(e*n)/n)||0,y:se(se(i*n)/n)||0}}(r):"function"==typeof h?h(r):r,u=d.x,f=void 0===u?0:u,p=d.y,m=void 0===p?0:p,g=r.hasOwnProperty("x"),_=r.hasOwnProperty("y"),b=bt,v=mt,y=window;if(c){var w=te(i),E="clientHeight",A="clientWidth";w===Wt(i)&&"static"!==Yt(w=Gt(i)).position&&"absolute"===a&&(E="scrollHeight",A="scrollWidth"),w=w,s!==mt&&(s!==bt&&s!==_t||o!==Et)||(v=gt,m-=w[E]-n.height,m*=l?1:-1),s!==bt&&(s!==mt&&s!==gt||o!==Et)||(b=_t,f-=w[A]-n.width,f*=l?1:-1)}var T,O=Object.assign({position:a},c&&he);return l?Object.assign({},O,((T={})[v]=_?"0":"",T[b]=g?"0":"",T.transform=(y.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",T)):Object.assign({},O,((e={})[v]=_?m+"px":"",e[b]=g?f+"px":"",e.transform="",e))}const ue={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:Ut(e.placement),variation:ce(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,de(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,de(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var fe={passive:!0};const pe={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=Wt(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,fe)})),a&&l.addEventListener("resize",i.update,fe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,fe)})),a&&l.removeEventListener("resize",i.update,fe)}},data:{}};var me={left:"right",right:"left",bottom:"top",top:"bottom"};function ge(t){return t.replace(/left|right|bottom|top/g,(function(t){return me[t]}))}var _e={start:"end",end:"start"};function be(t){return t.replace(/start|end/g,(function(t){return _e[t]}))}function ve(t){var e=Wt(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ye(t){return Vt(Gt(t)).left+ve(t).scrollLeft}function we(t){var e=Yt(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ee(t){return["html","body","#document"].indexOf(Rt(t))>=0?t.ownerDocument.body:zt(t)&&we(t)?t:Ee(Zt(t))}function Ae(t,e){var i;void 0===e&&(e=[]);var n=Ee(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=Wt(n),r=s?[o].concat(o.visualViewport||[],we(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Ae(Zt(r)))}function Te(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Oe(t,e){return e===Tt?Te(function(t){var e=Wt(t),i=Gt(t),n=e.visualViewport,s=i.clientWidth,o=i.clientHeight,r=0,a=0;return n&&(s=n.width,o=n.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(r=n.offsetLeft,a=n.offsetTop)),{width:s,height:o,x:r+ye(t),y:a}}(t)):zt(e)?function(t){var e=Vt(t);return e.top=e.top+t.clientTop,e.left=e.left+t.clientLeft,e.bottom=e.top+t.clientHeight,e.right=e.left+t.clientWidth,e.width=t.clientWidth,e.height=t.clientHeight,e.x=e.left,e.y=e.top,e}(e):Te(function(t){var e,i=Gt(t),n=ve(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ie(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ie(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ye(t),l=-n.scrollTop;return"rtl"===Yt(s||i).direction&&(a+=ie(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Gt(t)))}function Ce(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?Ut(s):null,r=s?ce(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case mt:e={x:a,y:i.y-n.height};break;case gt:e={x:a,y:i.y+i.height};break;case _t:e={x:i.x+i.width,y:l};break;case bt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?ee(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case wt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Et:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ke(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.boundary,r=void 0===o?At:o,a=i.rootBoundary,l=void 0===a?Tt:a,c=i.elementContext,h=void 0===c?Ot:c,d=i.altBoundary,u=void 0!==d&&d,f=i.padding,p=void 0===f?0:f,m=re("number"!=typeof p?p:ae(p,yt)),g=h===Ot?Ct:Ot,_=t.rects.popper,b=t.elements[u?g:h],v=function(t,e,i){var n="clippingParents"===e?function(t){var e=Ae(Zt(t)),i=["absolute","fixed"].indexOf(Yt(t).position)>=0&&zt(t)?te(t):t;return $t(i)?e.filter((function(t){return $t(t)&&Xt(t,i)&&"body"!==Rt(t)})):[]}(t):[].concat(e),s=[].concat(n,[i]),o=s[0],r=s.reduce((function(e,i){var n=Oe(t,i);return e.top=ie(n.top,e.top),e.right=ne(n.right,e.right),e.bottom=ne(n.bottom,e.bottom),e.left=ie(n.left,e.left),e}),Oe(t,o));return r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}($t(b)?b:b.contextElement||Gt(t.elements.popper),r,l),y=Vt(t.elements.reference),w=Ce({reference:y,element:_,strategy:"absolute",placement:s}),E=Te(Object.assign({},_,w)),A=h===Ot?E:y,T={top:v.top-A.top+m.top,bottom:A.bottom-v.bottom+m.bottom,left:v.left-A.left+m.left,right:A.right-v.right+m.right},O=t.modifiersData.offset;if(h===Ot&&O){var C=O[s];Object.keys(T).forEach((function(t){var e=[_t,gt].indexOf(t)>=0?1:-1,i=[mt,gt].indexOf(t)>=0?"y":"x";T[t]+=C[i]*e}))}return T}function Le(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?Lt:l,h=ce(n),d=h?a?kt:kt.filter((function(t){return ce(t)===h})):yt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ke(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[Ut(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const xe={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=Ut(g),b=l||(_!==g&&p?function(t){if(Ut(t)===vt)return[];var e=ge(t);return[be(t),e,be(e)]}(g):[ge(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(Ut(i)===vt?Le(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,E=new Map,A=!0,T=v[0],O=0;O=0,D=x?"width":"height",S=ke(e,{placement:C,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),N=x?L?_t:bt:L?gt:mt;y[D]>w[D]&&(N=ge(N));var I=ge(N),P=[];if(o&&P.push(S[k]<=0),a&&P.push(S[N]<=0,S[I]<=0),P.every((function(t){return t}))){T=C,A=!1;break}E.set(C,P)}if(A)for(var j=function(t){var e=v.find((function(e){var i=E.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},M=p?3:1;M>0&&"break"!==j(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function De(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function Se(t){return[mt,_t,gt,bt].some((function(e){return t[e]>=0}))}const Ne={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=De(r,n),c=De(a,s,o),h=Se(l),d=Se(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Ie={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=Lt.reduce((function(t,i){return t[i]=function(t,e,i){var n=Ut(t),s=[bt,mt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[bt,_t].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},Pe={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=Ce({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},je={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=Ut(e.placement),b=ce(e.placement),v=!b,y=ee(_),w="x"===y?"y":"x",E=e.modifiersData.popperOffsets,A=e.rects.reference,T=e.rects.popper,O="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,C={x:0,y:0};if(E){if(o||a){var k="y"===y?mt:bt,L="y"===y?gt:_t,x="y"===y?"height":"width",D=E[y],S=E[y]+g[k],N=E[y]-g[L],I=f?-T[x]/2:0,P=b===wt?A[x]:T[x],j=b===wt?-T[x]:-A[x],M=e.elements.arrow,H=f&&M?Kt(M):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},R=B[k],W=B[L],$=oe(0,A[x],H[x]),z=v?A[x]/2-I-$-R-O:P-$-R-O,q=v?-A[x]/2+I+$+W+O:j+$+W+O,F=e.elements.arrow&&te(e.elements.arrow),U=F?"y"===y?F.clientTop||0:F.clientLeft||0:0,V=e.modifiersData.offset?e.modifiersData.offset[e.placement][y]:0,K=E[y]+z-V-U,X=E[y]+q-V;if(o){var Y=oe(f?ne(S,K):S,D,f?ie(N,X):N);E[y]=Y,C[y]=Y-D}if(a){var Q="x"===y?mt:bt,G="x"===y?gt:_t,Z=E[w],J=Z+g[Q],tt=Z-g[G],et=oe(f?ne(J,K):J,Z,f?ie(tt,X):tt);E[w]=et,C[w]=et-Z}}e.modifiersData[n]=C}},requiresIfExists:["offset"]};function Me(t,e,i){void 0===i&&(i=!1);var n=zt(e);zt(e)&&function(t){var e=t.getBoundingClientRect();e.width,t.offsetWidth,e.height,t.offsetHeight}(e);var s,o,r=Gt(e),a=Vt(t),l={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(n||!n&&!i)&&(("body"!==Rt(e)||we(r))&&(l=(s=e)!==Wt(s)&&zt(s)?{scrollLeft:(o=s).scrollLeft,scrollTop:o.scrollTop}:ve(s)),zt(e)?((c=Vt(e)).x+=e.clientLeft,c.y+=e.clientTop):r&&(c.x=ye(r))),{x:a.left+l.scrollLeft-c.x,y:a.top+l.scrollTop-c.y,width:a.width,height:a.height}}function He(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var Be={placement:"bottom",modifiers:[],strategy:"absolute"};function Re(){for(var t=arguments.length,e=new Array(t),i=0;ij.on(t,"mouseover",d))),this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Je),this._element.classList.add(Je),j.trigger(this._element,"shown.bs.dropdown",t)}hide(){if(c(this._element)||!this._isShown(this._menu))return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){j.trigger(this._element,"hide.bs.dropdown",t).defaultPrevented||("ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._popper&&this._popper.destroy(),this._menu.classList.remove(Je),this._element.classList.remove(Je),this._element.setAttribute("aria-expanded","false"),U.removeDataAttribute(this._menu,"popper"),j.trigger(this._element,"hidden.bs.dropdown",t))}_getConfig(t){if(t={...this.constructor.Default,...U.getDataAttributes(this._element),...t},a(Ue,t,this.constructor.DefaultType),"object"==typeof t.reference&&!o(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ue.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(t){if(void 0===Fe)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;"parent"===this._config.reference?e=t:o(this._config.reference)?e=r(this._config.reference):"object"==typeof this._config.reference&&(e=this._config.reference);const i=this._getPopperConfig(),n=i.modifiers.find((t=>"applyStyles"===t.name&&!1===t.enabled));this._popper=qe(e,this._menu,i),n&&U.setDataAttribute(this._menu,"popper","static")}_isShown(t=this._element){return t.classList.contains(Je)}_getMenuElement(){return V.next(this._element,ei)[0]}_getPlacement(){const t=this._element.parentNode;if(t.classList.contains("dropend"))return ri;if(t.classList.contains("dropstart"))return ai;const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?ni:ii:e?oi:si}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return"static"===this._config.display&&(t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,..."function"==typeof this._config.popperConfig?this._config.popperConfig(t):this._config.popperConfig}}_selectMenuItem({key:t,target:e}){const i=V.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(l);i.length&&v(i,e,t===Ye,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(t&&(2===t.button||"keyup"===t.type&&"Tab"!==t.key))return;const e=V.find(ti);for(let i=0,n=e.length;ie+t)),this._setElementAttributes(di,"paddingRight",(e=>e+t)),this._setElementAttributes(ui,"marginRight",(e=>e-t))}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t)[e];t.style[e]=`${i(Number.parseFloat(s))}px`}))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,"paddingRight"),this._resetElementAttributes(di,"paddingRight"),this._resetElementAttributes(ui,"marginRight")}_saveInitialAttribute(t,e){const i=t.style[e];i&&U.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=U.getDataAttribute(t,e);void 0===i?t.style.removeProperty(e):(U.removeDataAttribute(t,e),t.style[e]=i)}))}_applyManipulationCallback(t,e){o(t)?e(t):V.find(t,this._element).forEach(e)}isOverflowing(){return this.getWidth()>0}}const pi={className:"modal-backdrop",isVisible:!0,isAnimated:!1,rootElement:"body",clickCallback:null},mi={className:"string",isVisible:"boolean",isAnimated:"boolean",rootElement:"(element|string)",clickCallback:"(function|null)"},gi="show",_i="mousedown.bs.backdrop";class bi{constructor(t){this._config=this._getConfig(t),this._isAppended=!1,this._element=null}show(t){this._config.isVisible?(this._append(),this._config.isAnimated&&u(this._getElement()),this._getElement().classList.add(gi),this._emulateAnimation((()=>{_(t)}))):_(t)}hide(t){this._config.isVisible?(this._getElement().classList.remove(gi),this._emulateAnimation((()=>{this.dispose(),_(t)}))):_(t)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_getConfig(t){return(t={...pi,..."object"==typeof t?t:{}}).rootElement=r(t.rootElement),a("backdrop",t,mi),t}_append(){this._isAppended||(this._config.rootElement.append(this._getElement()),j.on(this._getElement(),_i,(()=>{_(this._config.clickCallback)})),this._isAppended=!0)}dispose(){this._isAppended&&(j.off(this._element,_i),this._element.remove(),this._isAppended=!1)}_emulateAnimation(t){b(t,this._getElement(),this._config.isAnimated)}}const vi={trapElement:null,autofocus:!0},yi={trapElement:"element",autofocus:"boolean"},wi=".bs.focustrap",Ei="backward";class Ai{constructor(t){this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}activate(){const{trapElement:t,autofocus:e}=this._config;this._isActive||(e&&t.focus(),j.off(document,wi),j.on(document,"focusin.bs.focustrap",(t=>this._handleFocusin(t))),j.on(document,"keydown.tab.bs.focustrap",(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,j.off(document,wi))}_handleFocusin(t){const{target:e}=t,{trapElement:i}=this._config;if(e===document||e===i||i.contains(e))return;const n=V.focusableChildren(i);0===n.length?i.focus():this._lastTabNavDirection===Ei?n[n.length-1].focus():n[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?Ei:"forward")}_getConfig(t){return t={...vi,..."object"==typeof t?t:{}},a("focustrap",t,yi),t}}const Ti="modal",Oi="Escape",Ci={backdrop:!0,keyboard:!0,focus:!0},ki={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean"},Li="hidden.bs.modal",xi="show.bs.modal",Di="resize.bs.modal",Si="click.dismiss.bs.modal",Ni="keydown.dismiss.bs.modal",Ii="mousedown.dismiss.bs.modal",Pi="modal-open",ji="show",Mi="modal-static";class Hi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._dialog=V.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollBar=new fi}static get Default(){return Ci}static get NAME(){return Ti}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||j.trigger(this._element,xi,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isAnimated()&&(this._isTransitioning=!0),this._scrollBar.hide(),document.body.classList.add(Pi),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),j.on(this._dialog,Ii,(()=>{j.one(this._element,"mouseup.dismiss.bs.modal",(t=>{t.target===this._element&&(this._ignoreBackdropClick=!0)}))})),this._showBackdrop((()=>this._showElement(t))))}hide(){if(!this._isShown||this._isTransitioning)return;if(j.trigger(this._element,"hide.bs.modal").defaultPrevented)return;this._isShown=!1;const t=this._isAnimated();t&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),this._focustrap.deactivate(),this._element.classList.remove(ji),j.off(this._element,Si),j.off(this._dialog,Ii),this._queueCallback((()=>this._hideModal()),this._element,t)}dispose(){[window,this._dialog].forEach((t=>j.off(t,".bs.modal"))),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new bi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_getConfig(t){return t={...Ci,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Ti,t,ki),t}_showElement(t){const e=this._isAnimated(),i=V.findOne(".modal-body",this._dialog);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0,i&&(i.scrollTop=0),e&&u(this._element),this._element.classList.add(ji),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,j.trigger(this._element,"shown.bs.modal",{relatedTarget:t})}),this._dialog,e)}_setEscapeEvent(){this._isShown?j.on(this._element,Ni,(t=>{this._config.keyboard&&t.key===Oi?(t.preventDefault(),this.hide()):this._config.keyboard||t.key!==Oi||this._triggerBackdropTransition()})):j.off(this._element,Ni)}_setResizeEvent(){this._isShown?j.on(window,Di,(()=>this._adjustDialog())):j.off(window,Di)}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Pi),this._resetAdjustments(),this._scrollBar.reset(),j.trigger(this._element,Li)}))}_showBackdrop(t){j.on(this._element,Si,(t=>{this._ignoreBackdropClick?this._ignoreBackdropClick=!1:t.target===t.currentTarget&&(!0===this._config.backdrop?this.hide():"static"===this._config.backdrop&&this._triggerBackdropTransition())})),this._backdrop.show(t)}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(j.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const{classList:t,scrollHeight:e,style:i}=this._element,n=e>document.documentElement.clientHeight;!n&&"hidden"===i.overflowY||t.contains(Mi)||(n||(i.overflowY="hidden"),t.add(Mi),this._queueCallback((()=>{t.remove(Mi),n||this._queueCallback((()=>{i.overflowY=""}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;(!i&&t&&!m()||i&&!t&&m())&&(this._element.style.paddingLeft=`${e}px`),(i&&!t&&!m()||!i&&t&&m())&&(this._element.style.paddingRight=`${e}px`)}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Hi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}j.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=n(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),j.one(e,xi,(t=>{t.defaultPrevented||j.one(e,Li,(()=>{l(this)&&this.focus()}))}));const i=V.findOne(".modal.show");i&&Hi.getInstance(i).hide(),Hi.getOrCreateInstance(e).toggle(this)})),R(Hi),g(Hi);const Bi="offcanvas",Ri={backdrop:!0,keyboard:!0,scroll:!1},Wi={backdrop:"boolean",keyboard:"boolean",scroll:"boolean"},$i="show",zi=".offcanvas.show",qi="hidden.bs.offcanvas";class Fi extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get NAME(){return Bi}static get Default(){return Ri}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||j.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._element.style.visibility="visible",this._backdrop.show(),this._config.scroll||(new fi).hide(),this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add($i),this._queueCallback((()=>{this._config.scroll||this._focustrap.activate(),j.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(j.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.remove($i),this._backdrop.hide(),this._queueCallback((()=>{this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._element.style.visibility="hidden",this._config.scroll||(new fi).reset(),j.trigger(this._element,qi)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_getConfig(t){return t={...Ri,...U.getDataAttributes(this._element),..."object"==typeof t?t:{}},a(Bi,t,Wi),t}_initializeBackDrop(){return new bi({className:"offcanvas-backdrop",isVisible:this._config.backdrop,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:()=>this.hide()})}_initializeFocusTrap(){return new Ai({trapElement:this._element})}_addEventListeners(){j.on(this._element,"keydown.dismiss.bs.offcanvas",(t=>{this._config.keyboard&&"Escape"===t.key&&this.hide()}))}static jQueryInterface(t){return this.each((function(){const e=Fi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}j.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=n(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this))return;j.one(e,qi,(()=>{l(this)&&this.focus()}));const i=V.findOne(zi);i&&i!==e&&Fi.getInstance(i).hide(),Fi.getOrCreateInstance(e).toggle(this)})),j.on(window,"load.bs.offcanvas.data-api",(()=>V.find(zi).forEach((t=>Fi.getOrCreateInstance(t).show())))),R(Fi),g(Fi);const Ui=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Vi=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,Ki=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,Xi=(t,e)=>{const i=t.nodeName.toLowerCase();if(e.includes(i))return!Ui.has(i)||Boolean(Vi.test(t.nodeValue)||Ki.test(t.nodeValue));const n=e.filter((t=>t instanceof RegExp));for(let t=0,e=n.length;t{Xi(t,r)||i.removeAttribute(t.nodeName)}))}return n.body.innerHTML}const Qi="tooltip",Gi=new Set(["sanitize","allowList","sanitizeFn"]),Zi={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(array|string|function)",container:"(string|element|boolean)",fallbackPlacements:"array",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",allowList:"object",popperConfig:"(null|object|function)"},Ji={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},tn={animation:!0,template:'',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:[0,0],container:!1,fallbackPlacements:["top","right","bottom","left"],boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},en={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},nn="fade",sn="show",on="show",rn="out",an=".tooltip-inner",ln=".modal",cn="hide.bs.modal",hn="hover",dn="focus";class un extends B{constructor(t,e){if(void 0===Fe)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this._config=this._getConfig(e),this.tip=null,this._setListeners()}static get Default(){return tn}static get NAME(){return Qi}static get Event(){return en}static get DefaultType(){return Zi}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(t){if(this._isEnabled)if(t){const e=this._initializeOnDelegatedTarget(t);e._activeTrigger.click=!e._activeTrigger.click,e._isWithActiveTrigger()?e._enter(null,e):e._leave(null,e)}else{if(this.getTipElement().classList.contains(sn))return void this._leave(null,this);this._enter(null,this)}}dispose(){clearTimeout(this._timeout),j.off(this._element.closest(ln),cn,this._hideModalHandler),this.tip&&this.tip.remove(),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this.isWithContent()||!this._isEnabled)return;const t=j.trigger(this._element,this.constructor.Event.SHOW),e=h(this._element),i=null===e?this._element.ownerDocument.documentElement.contains(this._element):e.contains(this._element);if(t.defaultPrevented||!i)return;"tooltip"===this.constructor.NAME&&this.tip&&this.getTitle()!==this.tip.querySelector(an).innerHTML&&(this._disposePopper(),this.tip.remove(),this.tip=null);const n=this.getTipElement(),s=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME);n.setAttribute("id",s),this._element.setAttribute("aria-describedby",s),this._config.animation&&n.classList.add(nn);const o="function"==typeof this._config.placement?this._config.placement.call(this,n,this._element):this._config.placement,r=this._getAttachment(o);this._addAttachmentClass(r);const{container:a}=this._config;H.set(n,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||(a.append(n),j.trigger(this._element,this.constructor.Event.INSERTED)),this._popper?this._popper.update():this._popper=qe(this._element,n,this._getPopperConfig(r)),n.classList.add(sn);const l=this._resolvePossibleFunction(this._config.customClass);l&&n.classList.add(...l.split(" ")),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>{j.on(t,"mouseover",d)}));const c=this.tip.classList.contains(nn);this._queueCallback((()=>{const t=this._hoverState;this._hoverState=null,j.trigger(this._element,this.constructor.Event.SHOWN),t===rn&&this._leave(null,this)}),this.tip,c)}hide(){if(!this._popper)return;const t=this.getTipElement();if(j.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented)return;t.classList.remove(sn),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach((t=>j.off(t,"mouseover",d))),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1;const e=this.tip.classList.contains(nn);this._queueCallback((()=>{this._isWithActiveTrigger()||(this._hoverState!==on&&t.remove(),this._cleanTipClass(),this._element.removeAttribute("aria-describedby"),j.trigger(this._element,this.constructor.Event.HIDDEN),this._disposePopper())}),this.tip,e),this._hoverState=""}update(){null!==this._popper&&this._popper.update()}isWithContent(){return Boolean(this.getTitle())}getTipElement(){if(this.tip)return this.tip;const t=document.createElement("div");t.innerHTML=this._config.template;const e=t.children[0];return this.setContent(e),e.classList.remove(nn,sn),this.tip=e,this.tip}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),an)}_sanitizeAndSetContent(t,e,i){const n=V.findOne(i,t);e||!n?this.setElementContent(n,e):n.remove()}setElementContent(t,e){if(null!==t)return o(e)?(e=r(e),void(this._config.html?e.parentNode!==t&&(t.innerHTML="",t.append(e)):t.textContent=e.textContent)):void(this._config.html?(this._config.sanitize&&(e=Yi(e,this._config.allowList,this._config.sanitizeFn)),t.innerHTML=e):t.textContent=e)}getTitle(){const t=this._element.getAttribute("data-bs-original-title")||this._config.title;return this._resolvePossibleFunction(t)}updateAttachment(t){return"right"===t?"end":"left"===t?"start":t}_initializeOnDelegatedTarget(t,e){return e||this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return"function"==typeof t?t.call(this._element):t}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"onChange",enabled:!0,phase:"afterWrite",fn:t=>this._handlePopperPlacementChange(t)}],onFirstUpdate:t=>{t.options.placement!==t.placement&&this._handlePopperPlacementChange(t)}};return{...e,..."function"==typeof this._config.popperConfig?this._config.popperConfig(e):this._config.popperConfig}}_addAttachmentClass(t){this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(t)}`)}_getAttachment(t){return Ji[t.toUpperCase()]}_setListeners(){this._config.trigger.split(" ").forEach((t=>{if("click"===t)j.on(this._element,this.constructor.Event.CLICK,this._config.selector,(t=>this.toggle(t)));else if("manual"!==t){const e=t===hn?this.constructor.Event.MOUSEENTER:this.constructor.Event.FOCUSIN,i=t===hn?this.constructor.Event.MOUSELEAVE:this.constructor.Event.FOCUSOUT;j.on(this._element,e,this._config.selector,(t=>this._enter(t))),j.on(this._element,i,this._config.selector,(t=>this._leave(t)))}})),this._hideModalHandler=()=>{this._element&&this.hide()},j.on(this._element.closest(ln),cn,this._hideModalHandler),this._config.selector?this._config={...this._config,trigger:"manual",selector:""}:this._fixTitle()}_fixTitle(){const t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-bs-original-title");(t||"string"!==e)&&(this._element.setAttribute("data-bs-original-title",t||""),!t||this._element.getAttribute("aria-label")||this._element.textContent||this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))}_enter(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusin"===t.type?dn:hn]=!0),e.getTipElement().classList.contains(sn)||e._hoverState===on?e._hoverState=on:(clearTimeout(e._timeout),e._hoverState=on,e._config.delay&&e._config.delay.show?e._timeout=setTimeout((()=>{e._hoverState===on&&e.show()}),e._config.delay.show):e.show())}_leave(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusout"===t.type?dn:hn]=e._element.contains(t.relatedTarget)),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=rn,e._config.delay&&e._config.delay.hide?e._timeout=setTimeout((()=>{e._hoverState===rn&&e.hide()}),e._config.delay.hide):e.hide())}_isWithActiveTrigger(){for(const t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1}_getConfig(t){const e=U.getDataAttributes(this._element);return Object.keys(e).forEach((t=>{Gi.has(t)&&delete e[t]})),(t={...this.constructor.Default,...e,..."object"==typeof t&&t?t:{}}).container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),a(Qi,t,this.constructor.DefaultType),t.sanitize&&(t.template=Yi(t.template,t.allowList,t.sanitizeFn)),t}_getDelegateConfig(){const t={};for(const e in this._config)this.constructor.Default[e]!==this._config[e]&&(t[e]=this._config[e]);return t}_cleanTipClass(){const t=this.getTipElement(),e=new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`,"g"),i=t.getAttribute("class").match(e);null!==i&&i.length>0&&i.map((t=>t.trim())).forEach((e=>t.classList.remove(e)))}_getBasicClassPrefix(){return"bs-tooltip"}_handlePopperPlacementChange(t){const{state:e}=t;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null)}static jQueryInterface(t){return this.each((function(){const e=un.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(un);const fn={...un.Default,placement:"right",offset:[0,8],trigger:"click",content:"",template:''},pn={...un.DefaultType,content:"(string|element|function)"},mn={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"};class gn extends un{static get Default(){return fn}static get NAME(){return"popover"}static get Event(){return mn}static get DefaultType(){return pn}isWithContent(){return this.getTitle()||this._getContent()}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),".popover-header"),this._sanitizeAndSetContent(t,this._getContent(),".popover-body")}_getContent(){return this._resolvePossibleFunction(this._config.content)}_getBasicClassPrefix(){return"bs-popover"}static jQueryInterface(t){return this.each((function(){const e=gn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}g(gn);const _n="scrollspy",bn={offset:10,method:"auto",target:""},vn={offset:"number",method:"string",target:"(string|element)"},yn="active",wn=".nav-link, .list-group-item, .dropdown-item",En="position";class An extends B{constructor(t,e){super(t),this._scrollElement="BODY"===this._element.tagName?window:this._element,this._config=this._getConfig(e),this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,j.on(this._scrollElement,"scroll.bs.scrollspy",(()=>this._process())),this.refresh(),this._process()}static get Default(){return bn}static get NAME(){return _n}refresh(){const t=this._scrollElement===this._scrollElement.window?"offset":En,e="auto"===this._config.method?t:this._config.method,n=e===En?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),V.find(wn,this._config.target).map((t=>{const s=i(t),o=s?V.findOne(s):null;if(o){const t=o.getBoundingClientRect();if(t.width||t.height)return[U[e](o).top+n,s]}return null})).filter((t=>t)).sort(((t,e)=>t[0]-e[0])).forEach((t=>{this._offsets.push(t[0]),this._targets.push(t[1])}))}dispose(){j.off(this._scrollElement,".bs.scrollspy"),super.dispose()}_getConfig(t){return(t={...bn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}}).target=r(t.target)||document.documentElement,a(_n,t,vn),t}_getScrollTop(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop}_getScrollHeight(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}_getOffsetHeight(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height}_process(){const t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),i=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=i){const t=this._targets[this._targets.length-1];this._activeTarget!==t&&this._activate(t)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(let e=this._offsets.length;e--;)this._activeTarget!==this._targets[e]&&t>=this._offsets[e]&&(void 0===this._offsets[e+1]||t`${e}[data-bs-target="${t}"],${e}[href="${t}"]`)),i=V.findOne(e.join(","),this._config.target);i.classList.add(yn),i.classList.contains("dropdown-item")?V.findOne(".dropdown-toggle",i.closest(".dropdown")).classList.add(yn):V.parents(i,".nav, .list-group").forEach((t=>{V.prev(t,".nav-link, .list-group-item").forEach((t=>t.classList.add(yn))),V.prev(t,".nav-item").forEach((t=>{V.children(t,".nav-link").forEach((t=>t.classList.add(yn)))}))})),j.trigger(this._scrollElement,"activate.bs.scrollspy",{relatedTarget:t})}_clear(){V.find(wn,this._config.target).filter((t=>t.classList.contains(yn))).forEach((t=>t.classList.remove(yn)))}static jQueryInterface(t){return this.each((function(){const e=An.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(window,"load.bs.scrollspy.data-api",(()=>{V.find('[data-bs-spy="scroll"]').forEach((t=>new An(t)))})),g(An);const Tn="active",On="fade",Cn="show",kn=".active",Ln=":scope > li > .active";class xn extends B{static get NAME(){return"tab"}show(){if(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&this._element.classList.contains(Tn))return;let t;const e=n(this._element),i=this._element.closest(".nav, .list-group");if(i){const e="UL"===i.nodeName||"OL"===i.nodeName?Ln:kn;t=V.find(e,i),t=t[t.length-1]}const s=t?j.trigger(t,"hide.bs.tab",{relatedTarget:this._element}):null;if(j.trigger(this._element,"show.bs.tab",{relatedTarget:t}).defaultPrevented||null!==s&&s.defaultPrevented)return;this._activate(this._element,i);const o=()=>{j.trigger(t,"hidden.bs.tab",{relatedTarget:this._element}),j.trigger(this._element,"shown.bs.tab",{relatedTarget:t})};e?this._activate(e,e.parentNode,o):o()}_activate(t,e,i){const n=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?V.children(e,kn):V.find(Ln,e))[0],s=i&&n&&n.classList.contains(On),o=()=>this._transitionComplete(t,n,i);n&&s?(n.classList.remove(Cn),this._queueCallback(o,t,!0)):o()}_transitionComplete(t,e,i){if(e){e.classList.remove(Tn);const t=V.findOne(":scope > .dropdown-menu .active",e.parentNode);t&&t.classList.remove(Tn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}t.classList.add(Tn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),u(t),t.classList.contains(On)&&t.classList.add(Cn);let n=t.parentNode;if(n&&"LI"===n.nodeName&&(n=n.parentNode),n&&n.classList.contains("dropdown-menu")){const e=t.closest(".dropdown");e&&V.find(".dropdown-toggle",e).forEach((t=>t.classList.add(Tn))),t.setAttribute("aria-expanded",!0)}i&&i()}static jQueryInterface(t){return this.each((function(){const e=xn.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(document,"click.bs.tab.data-api",'[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this)||xn.getOrCreateInstance(this).show()})),g(xn);const Dn="toast",Sn="hide",Nn="show",In="showing",Pn={animation:"boolean",autohide:"boolean",delay:"number"},jn={animation:!0,autohide:!0,delay:5e3};class Mn extends B{constructor(t,e){super(t),this._config=this._getConfig(e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get DefaultType(){return Pn}static get Default(){return jn}static get NAME(){return Dn}show(){j.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(Sn),u(this._element),this._element.classList.add(Nn),this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.remove(In),j.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this._element.classList.contains(Nn)&&(j.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(In),this._queueCallback((()=>{this._element.classList.add(Sn),this._element.classList.remove(In),this._element.classList.remove(Nn),j.trigger(this._element,"hidden.bs.toast")}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this._element.classList.contains(Nn)&&this._element.classList.remove(Nn),super.dispose()}_getConfig(t){return t={...jn,...U.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}},a(Dn,t,this.constructor.DefaultType),t}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){j.on(this._element,"mouseover.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"mouseout.bs.toast",(t=>this._onInteraction(t,!1))),j.on(this._element,"focusin.bs.toast",(t=>this._onInteraction(t,!0))),j.on(this._element,"focusout.bs.toast",(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=Mn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(Mn),g(Mn),{Alert:W,Button:z,Carousel:st,Collapse:pt,Dropdown:hi,Modal:Hi,Offcanvas:Fi,Popover:gn,ScrollSpy:An,Tab:xn,Toast:Mn,Tooltip:un}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/docs/dev/deps/bootstrap-5.1.3/bootstrap.bundle.min.js.map b/docs/dev/deps/bootstrap-5.1.3/bootstrap.bundle.min.js.map new file mode 100644 index 000000000..7d78e32a6 --- /dev/null +++ b/docs/dev/deps/bootstrap-5.1.3/bootstrap.bundle.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../js/src/util/index.js","../../js/src/dom/event-handler.js","../../js/src/dom/data.js","../../js/src/base-component.js","../../js/src/util/component-functions.js","../../js/src/alert.js","../../js/src/button.js","../../js/src/dom/manipulator.js","../../js/src/dom/selector-engine.js","../../js/src/carousel.js","../../js/src/collapse.js","../../node_modules/@popperjs/core/lib/enums.js","../../node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindow.js","../../node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","../../node_modules/@popperjs/core/lib/modifiers/applyStyles.js","../../node_modules/@popperjs/core/lib/utils/getBasePlacement.js","../../node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","../../node_modules/@popperjs/core/lib/dom-utils/contains.js","../../node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","../../node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","../../node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","../../node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","../../node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","../../node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","../../node_modules/@popperjs/core/lib/utils/math.js","../../node_modules/@popperjs/core/lib/utils/within.js","../../node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","../../node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","../../node_modules/@popperjs/core/lib/utils/expandToHashMap.js","../../node_modules/@popperjs/core/lib/modifiers/arrow.js","../../node_modules/@popperjs/core/lib/utils/getVariation.js","../../node_modules/@popperjs/core/lib/modifiers/computeStyles.js","../../node_modules/@popperjs/core/lib/modifiers/eventListeners.js","../../node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","../../node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","../../node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","../../node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","../../node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","../../node_modules/@popperjs/core/lib/utils/rectToClientRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","../../node_modules/@popperjs/core/lib/utils/computeOffsets.js","../../node_modules/@popperjs/core/lib/utils/detectOverflow.js","../../node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","../../node_modules/@popperjs/core/lib/modifiers/flip.js","../../node_modules/@popperjs/core/lib/modifiers/hide.js","../../node_modules/@popperjs/core/lib/modifiers/offset.js","../../node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","../../node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","../../node_modules/@popperjs/core/lib/utils/getAltAxis.js","../../node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","../../node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","../../node_modules/@popperjs/core/lib/utils/orderModifiers.js","../../node_modules/@popperjs/core/lib/createPopper.js","../../node_modules/@popperjs/core/lib/utils/debounce.js","../../node_modules/@popperjs/core/lib/utils/mergeByName.js","../../node_modules/@popperjs/core/lib/popper-lite.js","../../node_modules/@popperjs/core/lib/popper.js","../../js/src/dropdown.js","../../js/src/util/scrollbar.js","../../js/src/util/backdrop.js","../../js/src/util/focustrap.js","../../js/src/modal.js","../../js/src/offcanvas.js","../../js/src/util/sanitizer.js","../../js/src/tooltip.js","../../js/src/popover.js","../../js/src/scrollspy.js","../../js/src/tab.js","../../js/src/toast.js","../../js/index.umd.js"],"names":["TRANSITION_END","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getSelectorFromElement","document","querySelector","getElementFromSelector","triggerTransitionEnd","dispatchEvent","Event","isElement","obj","jquery","nodeType","getElement","length","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","toString","call","match","toLowerCase","RegExp","test","TypeError","toUpperCase","isVisible","getClientRects","getComputedStyle","getPropertyValue","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","findShadowRoot","documentElement","attachShadow","getRootNode","root","ShadowRoot","parentNode","noop","reflow","offsetHeight","getjQuery","jQuery","window","body","DOMContentLoadedCallbacks","isRTL","dir","defineJQueryPlugin","plugin","callback","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","readyState","addEventListener","push","execute","executeAfterTransition","transitionElement","waitForTransition","emulatedDuration","transitionDuration","transitionDelay","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","getTransitionDurationFromElement","called","handler","target","removeEventListener","setTimeout","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","index","indexOf","listLength","Math","max","min","namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","customEventsRegex","nativeEvents","Set","getUidEvent","uid","getEvent","findHandler","events","delegationSelector","uidEventList","i","len","event","originalHandler","normalizeParams","originalTypeEvent","delegationFn","delegation","typeEvent","getTypeEvent","has","addHandler","oneOff","wrapFn","relatedTarget","delegateTarget","this","handlers","previousFn","replace","domElements","querySelectorAll","EventHandler","off","type","apply","bootstrapDelegationHandler","bootstrapHandler","removeHandler","Boolean","on","one","inNamespace","isNamespace","elementEvent","namespace","storeElementEvent","handlerKey","removeNamespacedHandlers","slice","keyHandlers","trigger","args","isNative","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","evt","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","createEvent","initEvent","CustomEvent","cancelable","key","defineProperty","get","preventDefault","elementMap","Map","Data","set","instance","instanceMap","size","console","error","Array","from","remove","delete","BaseComponent","constructor","_element","DATA_KEY","dispose","EVENT_KEY","getOwnPropertyNames","propertyName","_queueCallback","isAnimated","static","getInstance","VERSION","Error","enableDismissTrigger","component","method","clickEvent","tagName","closest","getOrCreateInstance","Alert","close","_destroyElement","each","data","undefined","SELECTOR_DATA_TOGGLE","Button","toggle","setAttribute","normalizeData","val","normalizeDataKey","chr","button","Manipulator","setDataAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","dataset","filter","pureKey","charAt","getDataAttribute","offset","rect","getBoundingClientRect","top","pageYOffset","left","pageXOffset","position","offsetTop","offsetLeft","SelectorEngine","find","concat","Element","prototype","findOne","children","child","matches","parents","ancestor","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","map","join","el","Default","interval","keyboard","slide","pause","wrap","touch","DefaultType","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","KEY_TO_DIRECTION","ArrowLeft","ArrowRight","EVENT_SLID","CLASS_NAME_ACTIVE","SELECTOR_ACTIVE_ITEM","Carousel","super","_items","_interval","_activeElement","_isPaused","_isSliding","touchTimeout","touchStartX","touchDeltaX","_config","_getConfig","_indicatorsElement","_touchSupported","navigator","maxTouchPoints","_pointerEvent","PointerEvent","_addEventListeners","_slide","nextWhenVisible","hidden","cycle","clearInterval","_updateInterval","setInterval","visibilityState","bind","to","activeIndex","_getItemIndex","order","_handleSwipe","absDeltax","abs","direction","_keydown","_addTouchEventListeners","hasPointerPenTouch","pointerType","start","clientX","touches","move","end","clearTimeout","itemImg","add","_getItemByOrder","isNext","_triggerSlideEvent","eventDirectionName","targetIndex","fromIndex","_setActiveIndicatorElement","activeIndicator","indicators","parseInt","elementInterval","defaultInterval","directionOrOrder","_directionToOrder","activeElementIndex","nextElement","nextElementIndex","isCycling","directionalClassName","orderClassName","_orderToDirection","triggerSlidEvent","completeCallBack","action","ride","carouselInterface","slideIndex","dataApiClickHandler","carousels","parent","CLASS_NAME_SHOW","CLASS_NAME_COLLAPSE","CLASS_NAME_COLLAPSING","CLASS_NAME_COLLAPSED","CLASS_NAME_DEEPER_CHILDREN","Collapse","_isTransitioning","_triggerArray","toggleList","elem","filterElement","foundElem","_selector","_initializeChildren","_addAriaAndCollapsedClass","_isShown","hide","show","activesData","actives","container","tempActiveData","elemActive","dimension","_getDimension","style","scrollSize","triggerArrayLength","selected","triggerArray","isOpen","bottom","right","auto","basePlacements","clippingParents","viewport","popper","reference","variationPlacements","reduce","acc","placement","placements","beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite","modifierPhases","getNodeName","nodeName","getWindow","node","ownerDocument","defaultView","isHTMLElement","HTMLElement","isShadowRoot","applyStyles$1","enabled","phase","_ref","state","elements","styles","assign","effect","_ref2","initialStyles","options","strategy","margin","arrow","hasOwnProperty","attribute","requires","getBasePlacement","includeScale","width","height","x","y","getLayoutRect","clientRect","offsetWidth","rootNode","isSameNode","host","isTableElement","getDocumentElement","getParentNode","assignedSlot","getTrueOffsetParent","offsetParent","getOffsetParent","isFirefox","userAgent","currentNode","css","transform","perspective","contain","willChange","getContainingBlock","getMainAxisFromPlacement","round","within","mathMax","mathMin","mergePaddingObject","paddingObject","expandToHashMap","hashMap","arrow$1","_state$modifiersData$","arrowElement","popperOffsets","modifiersData","basePlacement","axis","padding","rects","toPaddingObject","arrowRect","minProp","maxProp","endDiff","startDiff","arrowOffsetParent","clientSize","clientHeight","clientWidth","centerToReference","center","axisProp","centerOffset","_options$element","requiresIfExists","getVariation","unsetSides","mapToStyles","_Object$assign2","popperRect","variation","offsets","gpuAcceleration","adaptive","roundOffsets","_ref3","dpr","devicePixelRatio","roundOffsetsByDPR","_ref3$x","_ref3$y","hasX","hasY","sideX","sideY","win","heightProp","widthProp","_Object$assign","commonStyles","computeStyles$1","_ref4","_options$gpuAccelerat","_options$adaptive","_options$roundOffsets","passive","eventListeners","_options$scroll","scroll","_options$resize","resize","scrollParents","scrollParent","update","hash","getOppositePlacement","matched","getOppositeVariationPlacement","getWindowScroll","scrollLeft","scrollTop","getWindowScrollBarX","isScrollParent","_getComputedStyle","overflow","overflowX","overflowY","getScrollParent","listScrollParents","_element$ownerDocumen","isBody","visualViewport","updatedList","rectToClientRect","getClientRectFromMixedType","clippingParent","html","getViewportRect","clientTop","clientLeft","getInnerBoundingClientRect","winScroll","scrollWidth","scrollHeight","getDocumentRect","computeOffsets","commonX","commonY","mainAxis","detectOverflow","_options","_options$placement","_options$boundary","boundary","_options$rootBoundary","rootBoundary","_options$elementConte","elementContext","_options$altBoundary","altBoundary","_options$padding","altContext","clippingClientRect","mainClippingParents","clipperElement","getClippingParents","firstClippingParent","clippingRect","accRect","getClippingRect","contextElement","referenceClientRect","popperClientRect","elementClientRect","overflowOffsets","offsetData","multiply","computeAutoPlacement","flipVariations","_options$allowedAutoP","allowedAutoPlacements","allPlacements","allowedPlacements","overflows","sort","a","b","flip$1","_skip","_options$mainAxis","checkMainAxis","_options$altAxis","altAxis","checkAltAxis","specifiedFallbackPlacements","fallbackPlacements","_options$flipVariatio","preferredPlacement","oppositePlacement","getExpandedFallbackPlacements","referenceRect","checksMap","makeFallbackChecks","firstFittingPlacement","_basePlacement","isStartVariation","isVertical","mainVariationSide","altVariationSide","checks","every","check","_loop","_i","fittingPlacement","reset","getSideOffsets","preventedOffsets","isAnySideFullyClipped","some","side","hide$1","preventOverflow","referenceOverflow","popperAltOverflow","referenceClippingOffsets","popperEscapeOffsets","isReferenceHidden","hasPopperEscaped","offset$1","_options$offset","invertDistance","skidding","distance","distanceAndSkiddingToXY","_data$state$placement","popperOffsets$1","preventOverflow$1","_options$tether","tether","_options$tetherOffset","tetherOffset","isBasePlacement","tetherOffsetValue","mainSide","altSide","additive","minLen","maxLen","arrowPaddingObject","arrowPaddingMin","arrowPaddingMax","arrowLen","minOffset","maxOffset","clientOffset","offsetModifierValue","tetherMin","tetherMax","preventedOffset","_mainSide","_altSide","_offset","_min","_max","_preventedOffset","getCompositeRect","elementOrVirtualElement","isFixed","isOffsetParentAnElement","isElementScaled","modifiers","visited","result","modifier","dep","depModifier","DEFAULT_OPTIONS","areValidElements","_len","arguments","_key","popperGenerator","generatorOptions","_generatorOptions","_generatorOptions$def","defaultModifiers","_generatorOptions$def2","defaultOptions","pending","orderedModifiers","effectCleanupFns","isDestroyed","setOptions","setOptionsAction","cleanupModifierEffects","merged","orderModifiers","current","existing","m","_ref3$options","cleanupFn","forceUpdate","_state$elements","_state$orderedModifie","_state$orderedModifie2","Promise","resolve","then","destroy","onFirstUpdate","createPopper","computeStyles","applyStyles","flip","ESCAPE_KEY","SPACE_KEY","ARROW_UP_KEY","ARROW_DOWN_KEY","REGEXP_KEYDOWN","EVENT_CLICK_DATA_API","EVENT_KEYDOWN_DATA_API","SELECTOR_MENU","PLACEMENT_TOP","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","display","popperConfig","autoClose","Dropdown","_popper","_menu","_getMenuElement","_inNavbar","_detectNavbar","getParentFromElement","_createPopper","focus","_completeHide","Popper","referenceElement","_getPopperConfig","isDisplayStatic","_getPlacement","parentDropdown","isEnd","_getOffset","popperData","defaultBsPopperConfig","_selectMenuItem","items","toggles","context","composedPath","isMenuTarget","isActive","stopPropagation","getToggleButton","clearMenus","dataApiKeydownHandler","SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","ScrollBarHelper","getWidth","documentWidth","innerWidth","_disableOverFlow","_setElementAttributes","calculatedValue","_saveInitialAttribute","styleProp","scrollbarWidth","_applyManipulationCallback","_resetElementAttributes","actualValue","removeProperty","callBack","isOverflowing","className","rootElement","clickCallback","EVENT_MOUSEDOWN","Backdrop","_isAppended","_append","_getElement","_emulateAnimation","backdrop","createElement","append","trapElement","autofocus","TAB_NAV_BACKWARD","FocusTrap","_isActive","_lastTabNavDirection","activate","_handleFocusin","_handleKeydown","deactivate","shiftKey","EVENT_HIDDEN","EVENT_SHOW","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_KEYDOWN_DISMISS","EVENT_MOUSEDOWN_DISMISS","CLASS_NAME_OPEN","CLASS_NAME_STATIC","Modal","_dialog","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_ignoreBackdropClick","_scrollBar","_isAnimated","_adjustDialog","_setEscapeEvent","_setResizeEvent","_showBackdrop","_showElement","_hideModal","htmlElement","handleUpdate","modalBody","_triggerBackdropTransition","_resetAdjustments","currentTarget","isModalOverflowing","isBodyOverflowing","paddingLeft","paddingRight","showEvent","allReadyOpen","OPEN_SELECTOR","Offcanvas","visibility","blur","uriAttributes","SAFE_URL_PATTERN","DATA_URL_PATTERN","allowedAttribute","allowedAttributeList","attributeName","nodeValue","regExp","attributeRegex","sanitizeHtml","unsafeHtml","allowList","sanitizeFn","createdDocument","DOMParser","parseFromString","elementName","attributeList","allowedAttributes","innerHTML","DISALLOWED_ATTRIBUTES","animation","template","title","delay","customClass","sanitize","AttachmentMap","AUTO","TOP","RIGHT","BOTTOM","LEFT","area","br","col","code","div","em","hr","h1","h2","h3","h4","h5","h6","img","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","HIDE","HIDDEN","SHOW","SHOWN","INSERTED","CLICK","FOCUSIN","FOCUSOUT","MOUSEENTER","MOUSELEAVE","CLASS_NAME_FADE","HOVER_STATE_SHOW","HOVER_STATE_OUT","SELECTOR_TOOLTIP_INNER","SELECTOR_MODAL","EVENT_MODAL_HIDE","TRIGGER_HOVER","TRIGGER_FOCUS","Tooltip","_isEnabled","_timeout","_hoverState","_activeTrigger","tip","_setListeners","enable","disable","toggleEnabled","_initializeOnDelegatedTarget","click","_isWithActiveTrigger","_enter","_leave","getTipElement","_hideModalHandler","_disposePopper","isWithContent","shadowRoot","isInTheDom","getTitle","tipId","prefix","floor","random","getElementById","getUID","attachment","_getAttachment","_addAttachmentClass","_resolvePossibleFunction","prevHoverState","_cleanTipClass","setContent","_sanitizeAndSetContent","content","templateElement","setElementContent","textContent","updateAttachment","_getDelegateConfig","_handlePopperPlacementChange","_getBasicClassPrefix","eventIn","eventOut","_fixTitle","originalTitleType","dataAttributes","dataAttr","basicClassPrefixRegex","tabClass","token","tClass","Popover","_getContent","SELECTOR_LINK_ITEMS","METHOD_POSITION","ScrollSpy","_scrollElement","_offsets","_targets","_activeTarget","_scrollHeight","_process","refresh","autoMethod","offsetMethod","offsetBase","_getScrollTop","_getScrollHeight","targetSelector","targetBCR","item","_getOffsetHeight","innerHeight","maxScroll","_activate","_clear","queries","link","listGroup","navItem","spy","SELECTOR_ACTIVE","SELECTOR_ACTIVE_UL","Tab","listElement","itemSelector","hideEvent","complete","active","isTransitioning","_transitionComplete","dropdownChild","dropdownElement","dropdown","CLASS_NAME_HIDE","CLASS_NAME_SHOWING","autohide","Toast","_hasMouseInteraction","_hasKeyboardInteraction","_clearTimeout","_maybeScheduleHide","_onInteraction","isInteracting"],"mappings":"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","sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/\nconst stripNameRegex = /\\..*/\nconst stripUidRegex = /::\\d+$/\nconst eventRegistry = {} // Events storage\nlet uidEvent = 1\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n}\nconst customEventsRegex = /^(mouseenter|mouseleave)/i\nconst nativeEvents = new Set([\n 'click',\n 'dblclick',\n 'mouseup',\n 'mousedown',\n 'contextmenu',\n 'mousewheel',\n 'DOMMouseScroll',\n 'mouseover',\n 'mouseout',\n 'mousemove',\n 'selectstart',\n 'selectend',\n 'keydown',\n 'keypress',\n 'keyup',\n 'orientationchange',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'touchcancel',\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointerleave',\n 'pointercancel',\n 'gesturestart',\n 'gesturechange',\n 'gestureend',\n 'focus',\n 'blur',\n 'change',\n 'reset',\n 'select',\n 'submit',\n 'focusin',\n 'focusout',\n 'load',\n 'unload',\n 'beforeunload',\n 'resize',\n 'move',\n 'DOMContentLoaded',\n 'readystatechange',\n 'error',\n 'abort',\n 'scroll'\n])\n\n/**\n * ------------------------------------------------------------------------\n * Private methods\n * ------------------------------------------------------------------------\n */\n\nfunction getUidEvent(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getEvent(element) {\n const uid = getUidEvent(element)\n\n element.uidEvent = uid\n eventRegistry[uid] = eventRegistry[uid] || {}\n\n return eventRegistry[uid]\n}\n\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n event.delegateTarget = element\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn)\n }\n\n return fn.apply(element, [event])\n }\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector)\n\n for (let { target } = event; target && target !== this; target = target.parentNode) {\n for (let i = domElements.length; i--;) {\n if (domElements[i] === target) {\n event.delegateTarget = target\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n\n // To please ESLint\n return null\n }\n}\n\nfunction findHandler(events, handler, delegationSelector = null) {\n const uidEventList = Object.keys(events)\n\n for (let i = 0, len = uidEventList.length; i < len; i++) {\n const event = events[uidEventList[i]]\n\n if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {\n return event\n }\n }\n\n return null\n}\n\nfunction normalizeParams(originalTypeEvent, handler, delegationFn) {\n const delegation = typeof handler === 'string'\n const originalHandler = delegation ? delegationFn : handler\n\n let typeEvent = getTypeEvent(originalTypeEvent)\n const isNative = nativeEvents.has(typeEvent)\n\n if (!isNative) {\n typeEvent = originalTypeEvent\n }\n\n return [delegation, originalHandler, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n if (!handler) {\n handler = delegationFn\n delegationFn = null\n }\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (customEventsRegex.test(originalTypeEvent)) {\n const wrapFn = fn => {\n return function (event) {\n if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {\n return fn.call(this, event)\n }\n }\n }\n\n if (delegationFn) {\n delegationFn = wrapFn(delegationFn)\n } else {\n handler = wrapFn(handler)\n }\n }\n\n const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)\n const events = getEvent(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)\n\n if (previousFn) {\n previousFn.oneOff = previousFn.oneOff && oneOff\n\n return\n }\n\n const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = delegation ?\n bootstrapDelegationHandler(element, handler, delegationFn) :\n bootstrapHandler(element, handler)\n\n fn.delegationSelector = delegation ? handler : null\n fn.originalHandler = originalHandler\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, delegation)\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector)\n\n if (!fn) {\n return\n }\n\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))\n delete events[typeEvent][fn.uidEvent]\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {}\n\n Object.keys(storeElementEvent).forEach(handlerKey => {\n if (handlerKey.includes(namespace)) {\n const event = storeElementEvent[handlerKey]\n\n removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)\n }\n })\n}\n\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '')\n return customEvents[event] || event\n}\n\nconst EventHandler = {\n on(element, event, handler, delegationFn) {\n addHandler(element, event, handler, delegationFn, false)\n },\n\n one(element, event, handler, delegationFn) {\n addHandler(element, event, handler, delegationFn, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFn) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getEvent(element)\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof originalHandler !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!events || !events[typeEvent]) {\n return\n }\n\n removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)\n return\n }\n\n if (isNamespace) {\n Object.keys(events).forEach(elementEvent => {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n })\n }\n\n const storeElementEvent = events[typeEvent] || {}\n Object.keys(storeElementEvent).forEach(keyHandlers => {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n const event = storeElementEvent[keyHandlers]\n\n removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)\n }\n })\n },\n\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null\n }\n\n const $ = getjQuery()\n const typeEvent = getTypeEvent(event)\n const inNamespace = event !== typeEvent\n const isNative = nativeEvents.has(typeEvent)\n\n let jQueryEvent\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n let evt = null\n\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args)\n\n $(element).trigger(jQueryEvent)\n bubbles = !jQueryEvent.isPropagationStopped()\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()\n defaultPrevented = jQueryEvent.isDefaultPrevented()\n }\n\n if (isNative) {\n evt = document.createEvent('HTMLEvents')\n evt.initEvent(typeEvent, bubbles, true)\n } else {\n evt = new CustomEvent(event, {\n bubbles,\n cancelable: true\n })\n }\n\n // merge custom information in our event\n if (typeof args !== 'undefined') {\n Object.keys(args).forEach(key => {\n Object.defineProperty(evt, key, {\n get() {\n return args[key]\n }\n })\n })\n }\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nexport default EventHandler\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst elementMap = new Map()\n\nexport default {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map())\n }\n\n const instanceMap = elementMap.get(element)\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)\n return\n }\n\n instanceMap.set(key, instance)\n },\n\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null\n }\n\n return null\n },\n\n remove(element, key) {\n if (!elementMap.has(element)) {\n return\n }\n\n const instanceMap = elementMap.get(element)\n\n instanceMap.delete(key)\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element)\n }\n }\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data'\nimport {\n executeAfterTransition,\n getElement\n} from './util/index'\nimport EventHandler from './dom/event-handler'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst VERSION = '5.1.3'\n\nclass BaseComponent {\n constructor(element) {\n element = getElement(element)\n\n if (!element) {\n return\n }\n\n this._element = element\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n Object.getOwnPropertyNames(this).forEach(propertyName => {\n this[propertyName] = null\n })\n }\n\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n /** Static */\n\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY)\n }\n\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)\n }\n\n static get VERSION() {\n return VERSION\n }\n\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\n }\n\n static get DATA_KEY() {\n return `bs.${this.NAME}`\n }\n\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`\n }\n}\n\nexport default BaseComponent\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { getElementFromSelector, isDisabled } from './index'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'alert'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_CLOSE = `close${EVENT_KEY}`\nconst EVENT_CLOSED = `closed${EVENT_KEY}`\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Alert extends BaseComponent {\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)\n\n if (closeEvent.defaultPrevented) {\n return\n }\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated)\n }\n\n // Private\n _destroyElement() {\n this._element.remove()\n EventHandler.trigger(this._element, EVENT_CLOSED)\n this.dispose()\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nenableDismissTrigger(Alert, 'close')\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Alert to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'button'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\n\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Button extends BaseComponent {\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this)\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n event.preventDefault()\n\n const button = event.target.closest(SELECTOR_DATA_TOGGLE)\n const data = Button.getOrCreateInstance(button)\n\n data.toggle()\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Button to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(val) {\n if (val === 'true') {\n return true\n }\n\n if (val === 'false') {\n return false\n }\n\n if (val === Number(val).toString()) {\n return Number(val)\n }\n\n if (val === '' || val === 'null') {\n return null\n }\n\n return val\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n\n Object.keys(element.dataset)\n .filter(key => key.startsWith('bs'))\n .forEach(key => {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)\n attributes[pureKey] = normalizeData(element.dataset[key])\n })\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n },\n\n offset(element) {\n const rect = element.getBoundingClientRect()\n\n return {\n top: rect.top + window.pageYOffset,\n left: rect.left + window.pageXOffset\n }\n },\n\n position(element) {\n return {\n top: element.offsetTop,\n left: element.offsetLeft\n }\n }\n}\n\nexport default Manipulator\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible } from '../util/index'\n\nconst NODE_TEXT = 3\n\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector))\n },\n\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector)\n },\n\n children(element, selector) {\n return [].concat(...element.children)\n .filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n\n let ancestor = element.parentNode\n\n while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {\n if (ancestor.matches(selector)) {\n parents.push(ancestor)\n }\n\n ancestor = ancestor.parentNode\n }\n\n return parents\n },\n\n prev(element, selector) {\n let previous = element.previousElementSibling\n\n while (previous) {\n if (previous.matches(selector)) {\n return [previous]\n }\n\n previous = previous.previousElementSibling\n }\n\n return []\n },\n\n next(element, selector) {\n let next = element.nextElementSibling\n\n while (next) {\n if (next.matches(selector)) {\n return [next]\n }\n\n next = next.nextElementSibling\n }\n\n return []\n },\n\n focusableChildren(element) {\n const focusables = [\n 'a',\n 'button',\n 'input',\n 'textarea',\n 'select',\n 'details',\n '[tabindex]',\n '[contenteditable=\"true\"]'\n ].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(', ')\n\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))\n }\n}\n\nexport default SelectorEngine\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isRTL,\n isVisible,\n getNextActiveElement,\n reflow,\n triggerTransitionEnd,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'carousel'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n slide: false,\n pause: 'hover',\n wrap: true,\n touch: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)',\n keyboard: 'boolean',\n slide: '(boolean|string)',\n pause: '(string|boolean)',\n wrap: 'boolean',\n touch: 'boolean'\n}\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\n\nconst EVENT_SLIDE = `slide${EVENT_KEY}`\nconst EVENT_SLID = `slid${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`\nconst EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY}`\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY}`\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_CAROUSEL = 'carousel'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_SLIDE = 'slide'\nconst CLASS_NAME_END = 'carousel-item-end'\nconst CLASS_NAME_START = 'carousel-item-start'\nconst CLASS_NAME_NEXT = 'carousel-item-next'\nconst CLASS_NAME_PREV = 'carousel-item-prev'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ACTIVE_ITEM = '.active.carousel-item'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_INDICATOR = '[data-bs-target]'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._items = null\n this._interval = null\n this._activeElement = null\n this._isPaused = false\n this._isSliding = false\n this.touchTimeout = null\n this.touchStartX = 0\n this.touchDeltaX = 0\n\n this._config = this._getConfig(config)\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n this._pointerEvent = Boolean(window.PointerEvent)\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next()\n }\n }\n\n prev() {\n this._slide(ORDER_PREV)\n }\n\n pause(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) {\n triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config && this._config.interval && !this._isPaused) {\n this._updateInterval()\n\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index))\n return\n }\n\n if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const order = index > activeIndex ?\n ORDER_NEXT :\n ORDER_PREV\n\n this._slide(order, this._items[index])\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _handleSwipe() {\n const absDeltax = Math.abs(this.touchDeltaX)\n\n if (absDeltax <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltax / this.touchDeltaX\n\n this.touchDeltaX = 0\n\n if (!direction) {\n return\n }\n\n this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT)\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER, event => this.pause(event))\n EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event))\n }\n\n if (this._config.touch && this._touchSupported) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n const hasPointerPenTouch = event => {\n return this._pointerEvent &&\n (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)\n }\n\n const start = event => {\n if (hasPointerPenTouch(event)) {\n this.touchStartX = event.clientX\n } else if (!this._pointerEvent) {\n this.touchStartX = event.touches[0].clientX\n }\n }\n\n const move = event => {\n // ensure swiping with one touch and not pinching\n this.touchDeltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this.touchStartX\n }\n\n const end = event => {\n if (hasPointerPenTouch(event)) {\n this.touchDeltaX = event.clientX - this.touchStartX\n }\n\n this._handleSwipe()\n if (this._config.pause === 'hover') {\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n\n this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n }\n\n SelectorEngine.find(SELECTOR_ITEM_IMG, this._element).forEach(itemImg => {\n EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault())\n })\n\n if (this._pointerEvent) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => end(event))\n }\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n const direction = KEY_TO_DIRECTION[event.key]\n if (direction) {\n event.preventDefault()\n this._slide(direction)\n }\n }\n\n _getItemIndex(element) {\n this._items = element && element.parentNode ?\n SelectorEngine.find(SELECTOR_ITEM, element.parentNode) :\n []\n\n return this._items.indexOf(element)\n }\n\n _getItemByOrder(order, activeElement) {\n const isNext = order === ORDER_NEXT\n return getNextActiveElement(this._items, activeElement, isNext, this._config.wrap)\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex(SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element))\n\n return EventHandler.trigger(this._element, EVENT_SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)\n\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE)\n activeIndicator.removeAttribute('aria-current')\n\n const indicators = SelectorEngine.find(SELECTOR_INDICATOR, this._indicatorsElement)\n\n for (let i = 0; i < indicators.length; i++) {\n if (Number.parseInt(indicators[i].getAttribute('data-bs-slide-to'), 10) === this._getItemIndex(element)) {\n indicators[i].classList.add(CLASS_NAME_ACTIVE)\n indicators[i].setAttribute('aria-current', 'true')\n break\n }\n }\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n if (elementInterval) {\n this._config.defaultInterval = this._config.defaultInterval || this._config.interval\n this._config.interval = elementInterval\n } else {\n this._config.interval = this._config.defaultInterval || this._config.interval\n }\n }\n\n _slide(directionOrOrder, element) {\n const order = this._directionToOrder(directionOrOrder)\n const activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || this._getItemByOrder(order, activeElement)\n\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n const isNext = order === ORDER_NEXT\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n const eventDirectionName = this._orderToDirection(order)\n\n if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) {\n this._isSliding = false\n return\n }\n\n if (this._isSliding) {\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n this._activeElement = nextElement\n\n const triggerSlidEvent = () => {\n EventHandler.trigger(this._element, EVENT_SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n }\n\n if (this._element.classList.contains(CLASS_NAME_SLIDE)) {\n nextElement.classList.add(orderClassName)\n\n reflow(nextElement)\n\n activeElement.classList.add(directionalClassName)\n nextElement.classList.add(directionalClassName)\n\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)\n\n this._isSliding = false\n\n setTimeout(triggerSlidEvent, 0)\n }\n\n this._queueCallback(completeCallBack, activeElement, true)\n } else {\n activeElement.classList.remove(CLASS_NAME_ACTIVE)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n this._isSliding = false\n triggerSlidEvent()\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _directionToOrder(direction) {\n if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) {\n return direction\n }\n\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT\n }\n\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV\n }\n\n _orderToDirection(order) {\n if (![ORDER_NEXT, ORDER_PREV].includes(order)) {\n return order\n }\n\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT\n }\n\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT\n }\n\n // Static\n\n static carouselInterface(element, config) {\n const data = Carousel.getOrCreateInstance(element, config)\n\n let { _config } = data\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n\n data[action]()\n } else if (_config.interval && _config.ride) {\n data.pause()\n data.cycle()\n }\n }\n\n static jQueryInterface(config) {\n return this.each(function () {\n Carousel.carouselInterface(this, config)\n })\n }\n\n static dataApiClickHandler(event) {\n const target = getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n const config = {\n ...Manipulator.getDataAttributes(target),\n ...Manipulator.getDataAttributes(this)\n }\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel.carouselInterface(target, config)\n\n if (slideIndex) {\n Carousel.getInstance(target).to(slideIndex)\n }\n\n event.preventDefault()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (let i = 0, len = carousels.length; i < len; i++) {\n Carousel.carouselInterface(carousels[i], Carousel.getInstance(carousels[i]))\n }\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Carousel to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElement,\n getSelectorFromElement,\n getElementFromSelector,\n reflow,\n typeCheckConfig\n} from './util/index'\nimport Data from './dom/data'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst Default = {\n toggle: true,\n parent: null\n}\n\nconst DefaultType = {\n toggle: 'boolean',\n parent: '(null|element)'\n}\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_COLLAPSE = 'collapse'\nconst CLASS_NAME_COLLAPSING = 'collapsing'\nconst CLASS_NAME_COLLAPSED = 'collapsed'\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._isTransitioning = false\n this._config = this._getConfig(config)\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (let i = 0, len = toggleList.length; i < len; i++) {\n const elem = toggleList[i]\n const selector = getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElem => foundElem === this._element)\n\n if (selector !== null && filterElement.length) {\n this._selector = selector\n this._triggerArray.push(elem)\n }\n }\n\n this._initializeChildren()\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle() {\n if (this._isShown()) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning || this._isShown()) {\n return\n }\n\n let actives = []\n let activesData\n\n if (this._config.parent) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n actives = SelectorEngine.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem)) // remove children if greater depth\n }\n\n const container = SelectorEngine.findOne(this._selector)\n if (actives.length) {\n const tempActiveData = actives.find(elem => container !== elem)\n activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null\n\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n actives.forEach(elemActive => {\n if (container !== elemActive) {\n Collapse.getOrCreateInstance(elemActive, { toggle: false }).hide()\n }\n\n if (!activesData) {\n Data.set(elemActive, DATA_KEY, null)\n }\n })\n\n const dimension = this._getDimension()\n\n this._element.classList.remove(CLASS_NAME_COLLAPSE)\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n\n this._element.style[dimension] = 0\n\n this._addAriaAndCollapsedClass(this._triggerArray, true)\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n this._element.style[dimension] = ''\n\n EventHandler.trigger(this._element, EVENT_SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n this._queueCallback(complete, this._element, true)\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n if (startEvent.defaultPrevented) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n const triggerArrayLength = this._triggerArray.length\n for (let i = 0; i < triggerArrayLength; i++) {\n const trigger = this._triggerArray[i]\n const elem = getElementFromSelector(trigger)\n\n if (elem && !this._isShown(elem)) {\n this._addAriaAndCollapsedClass([trigger], false)\n }\n }\n\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n this._queueCallback(complete, this._element, true)\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT\n }\n\n _initializeChildren() {\n if (!this._config.parent) {\n return\n }\n\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n SelectorEngine.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem))\n .forEach(element => {\n const selected = getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n })\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n triggerArray.forEach(elem => {\n if (isOpen) {\n elem.classList.remove(CLASS_NAME_COLLAPSED)\n } else {\n elem.classList.add(CLASS_NAME_COLLAPSED)\n }\n\n elem.setAttribute('aria-expanded', isOpen)\n })\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n const data = Collapse.getOrCreateInstance(this, _config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {\n event.preventDefault()\n }\n\n const selector = getSelectorFromElement(this)\n const selectorElements = SelectorEngine.find(selector)\n\n selectorElements.forEach(element => {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n })\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Collapse to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Collapse)\n\nexport default Collapse\n","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","// import { isHTMLElement } from './instanceOf';\nexport default function getBoundingClientRect(element, // eslint-disable-next-line unused-imports/no-unused-vars\nincludeScale) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n var rect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1; // FIXME:\n // `offsetWidth` returns an integer while `getBoundingClientRect`\n // returns a float. This results in `scaleX` or `scaleY` being\n // non-1 when it should be for elements that aren't a full pixel in\n // width or height.\n // if (isHTMLElement(element) && includeScale) {\n // const offsetHeight = element.offsetHeight;\n // const offsetWidth = element.offsetWidth;\n // // Do not attempt to divide by 0, otherwise we get `Infinity` as scale\n // // Fallback to 1 in case both values are `0`\n // if (offsetWidth > 0) {\n // scaleX = rect.width / offsetWidth || 1;\n // }\n // if (offsetHeight > 0) {\n // scaleY = rect.height / offsetHeight || 1;\n // }\n // }\n\n return {\n width: rect.width / scaleX,\n height: rect.height / scaleY,\n top: rect.top / scaleY,\n right: rect.right / scaleX,\n bottom: rect.bottom / scaleY,\n left: rect.left / scaleX,\n x: rect.left / scaleX,\n y: rect.top / scaleY\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1;\n var isIE = navigator.userAgent.indexOf('Trident') !== -1;\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport default function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport within from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (process.env.NODE_ENV !== \"production\") {\n if (!isHTMLElement(arrowElement)) {\n console.error(['Popper: \"arrow\" element must be an HTMLElement (not an SVGElement).', 'To use an SVG arrow, wrap it in an HTMLElement that will be used as', 'the arrow.'].join(' '));\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(['Popper: \"arrow\" modifier\\'s `element` must be a child of the popper', 'element.'].join(' '));\n }\n\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref) {\n var x = _ref.x,\n y = _ref.y;\n var win = window;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(round(x * dpr) / dpr) || 0,\n y: round(round(y * dpr) / dpr) || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets;\n\n var _ref3 = roundOffsets === true ? roundOffsetsByDPR(offsets) : typeof roundOffsets === 'function' ? roundOffsets(offsets) : offsets,\n _ref3$x = _ref3.x,\n x = _ref3$x === void 0 ? 0 : _ref3$x,\n _ref3$y = _ref3.y,\n y = _ref3$y === void 0 ? 0 : _ref3$y;\n\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom; // $FlowFixMe[prop-missing]\n\n y -= offsetParent[heightProp] - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right; // $FlowFixMe[prop-missing]\n\n x -= offsetParent[widthProp] - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref4) {\n var state = _ref4.state,\n options = _ref4.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n\n if (process.env.NODE_ENV !== \"production\") {\n var transitionProperty = getComputedStyle(state.elements.popper).transitionProperty || '';\n\n if (adaptive && ['transform', 'top', 'right', 'bottom', 'left'].some(function (property) {\n return transitionProperty.indexOf(property) >= 0;\n })) {\n console.warn(['Popper: Detected CSS transitions on at least one of the following', 'CSS properties: \"transform\", \"top\", \"right\", \"bottom\", \"left\".', '\\n\\n', 'Disable the \"computeStyles\" modifier\\'s `adaptive` option to allow', 'for smooth transitions, or remove these properties from the CSS', 'transition declaration on the popper element if only transitioning', 'opacity or background-color for example.', '\\n\\n', 'We recommend using the popper element as a wrapper around an inner', 'element that can have any CSS property transitioned for animations.'].join(' '));\n }\n }\n\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element) {\n var rect = getBoundingClientRect(element);\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isHTMLElement(clippingParent) ? getInnerBoundingClientRect(clippingParent) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nexport default function getViewportRect(element) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper\n // can be obscured underneath it.\n // Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even\n // if it isn't open, so if this isn't available, the popper will be detected\n // to overflow the bottom of the screen too early.\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently)\n // In Chrome, it returns a value very close to 0 (+/-) but contains rounding\n // errors due to floating point numbers, so we need to check precision.\n // Safari returns a number <= 0, usually < -1 when pinch-zoomed\n // Feature detection fails in mobile emulation mode in Chrome.\n // Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <\n // 0.001\n // Fallback here: \"Not Safari\" userAgent\n\n if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n\n if (process.env.NODE_ENV !== \"production\") {\n console.error(['Popper: The `allowedAutoPlacements` option did not allow any', 'placements. Ensure the `placement` option matches the variation', 'of the allowed placements.', 'For example, \"auto\" cannot be used to allow \"bottom-start\".', 'Use \"auto-start\" instead.'].join(' '));\n }\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\";\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport within from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { max as mathMax, min as mathMin } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis || checkAltAxis) {\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = popperOffsets[mainAxis] + overflow[mainSide];\n var max = popperOffsets[mainAxis] - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - tetherOffsetValue : minLen - arrowLen - arrowPaddingMin - tetherOffsetValue;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + tetherOffsetValue : maxLen + arrowLen + arrowPaddingMax + tetherOffsetValue;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = state.modifiersData.offset ? state.modifiersData.offset[state.placement][mainAxis] : 0;\n var tetherMin = popperOffsets[mainAxis] + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = popperOffsets[mainAxis] + maxOffset - offsetModifierValue;\n\n if (checkMainAxis) {\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var _preventedOffset = within(tether ? mathMin(_min, tetherMin) : _min, _offset, tether ? mathMax(_max, tetherMax) : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = rect.width / element.offsetWidth || 1;\n var scaleY = rect.height / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport getComputedStyle from \"./dom-utils/getComputedStyle.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport validateModifiers from \"./utils/validateModifiers.js\";\nimport uniqueBy from \"./utils/uniqueBy.js\";\nimport getBasePlacement from \"./utils/getBasePlacement.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nimport { auto } from \"./enums.js\";\nvar INVALID_ELEMENT_ERROR = 'Popper: Invalid reference or popper argument provided. They must be either a DOM element or virtual element.';\nvar INFINITE_LOOP_ERROR = 'Popper: An infinite loop in the modifiers cycle has been detected! The cycle has been interrupted to prevent a browser crash.';\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n }); // Validate the provided modifiers so that the consumer will get warned\n // if one of the modifiers is invalid for any reason\n\n if (process.env.NODE_ENV !== \"production\") {\n var modifiers = uniqueBy([].concat(orderedModifiers, state.options.modifiers), function (_ref) {\n var name = _ref.name;\n return name;\n });\n validateModifiers(modifiers);\n\n if (getBasePlacement(state.options.placement) === auto) {\n var flipModifier = state.orderedModifiers.find(function (_ref2) {\n var name = _ref2.name;\n return name === 'flip';\n });\n\n if (!flipModifier) {\n console.error(['Popper: \"auto\" placements require the \"flip\" modifier be', 'present and enabled to work.'].join(' '));\n }\n }\n\n var _getComputedStyle = getComputedStyle(popper),\n marginTop = _getComputedStyle.marginTop,\n marginRight = _getComputedStyle.marginRight,\n marginBottom = _getComputedStyle.marginBottom,\n marginLeft = _getComputedStyle.marginLeft; // We no longer take into account `margins` on the popper, and it can\n // cause bugs with positioning, so we'll warn the consumer\n\n\n if ([marginTop, marginRight, marginBottom, marginLeft].some(function (margin) {\n return parseFloat(margin);\n })) {\n console.warn(['Popper: CSS \"margin\" styles cannot be used to apply padding', 'between the popper and its reference element or boundary.', 'To replicate margin, use the `offset` modifier, as well as', 'the `padding` option in the `preventOverflow` and `flip`', 'modifiers.'].join(' '));\n }\n }\n\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(INVALID_ELEMENT_ERROR);\n }\n\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n var __debug_loops__ = 0;\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (process.env.NODE_ENV !== \"production\") {\n __debug_loops__ += 1;\n\n if (__debug_loops__ > 100) {\n console.error(INFINITE_LOOP_ERROR);\n break;\n }\n }\n\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(INVALID_ELEMENT_ERROR);\n }\n\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref3) {\n var name = _ref3.name,\n _ref3$options = _ref3.options,\n options = _ref3$options === void 0 ? {} : _ref3$options,\n effect = _ref3.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\n\nimport {\n defineJQueryPlugin,\n getElement,\n getElementFromSelector,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'dropdown'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ESCAPE_KEY = 'Escape'\nconst SPACE_KEY = 'Space'\nconst TAB_KEY = 'Tab'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button\n\nconst REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_DROPUP = 'dropup'\nconst CLASS_NAME_DROPEND = 'dropend'\nconst CLASS_NAME_DROPSTART = 'dropstart'\nconst CLASS_NAME_NAVBAR = 'navbar'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]'\nconst SELECTOR_MENU = '.dropdown-menu'\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav'\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'\n\nconst Default = {\n offset: [0, 2],\n boundary: 'clippingParents',\n reference: 'toggle',\n display: 'dynamic',\n popperConfig: null,\n autoClose: true\n}\n\nconst DefaultType = {\n offset: '(array|string|function)',\n boundary: '(string|element)',\n reference: '(string|element|object)',\n display: 'string',\n popperConfig: '(null|object|function)',\n autoClose: '(boolean|string)'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._popper = null\n this._config = this._getConfig(config)\n this._menu = this._getMenuElement()\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown(this._menu)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n const parent = Dropdown.getParentFromElement(this._element)\n // Totally disable Popper for Dropdowns in Navbar\n if (this._inNavbar) {\n Manipulator.setDataAttribute(this._menu, 'popper', 'none')\n } else {\n this._createPopper(parent)\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement &&\n !parent.closest(SELECTOR_NAVBAR_NAV)) {\n [].concat(...document.body.children)\n .forEach(elem => EventHandler.on(elem, 'mouseover', noop))\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n this._menu.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)\n }\n\n hide() {\n if (isDisabled(this._element) || !this._isShown(this._menu)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n this._completeHide(relatedTarget)\n }\n\n dispose() {\n if (this._popper) {\n this._popper.destroy()\n }\n\n super.dispose()\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Private\n\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n [].concat(...document.body.children)\n .forEach(elem => EventHandler.off(elem, 'mouseover', noop))\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\n this._menu.classList.remove(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._element.setAttribute('aria-expanded', 'false')\n Manipulator.removeDataAttribute(this._menu, 'popper')\n EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)\n }\n\n _getConfig(config) {\n config = {\n ...this.constructor.Default,\n ...Manipulator.getDataAttributes(this._element),\n ...config\n }\n\n typeCheckConfig(NAME, config, this.constructor.DefaultType)\n\n if (typeof config.reference === 'object' && !isElement(config.reference) &&\n typeof config.reference.getBoundingClientRect !== 'function'\n ) {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`)\n }\n\n return config\n }\n\n _createPopper(parent) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = parent\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference)\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference\n }\n\n const popperConfig = this._getPopperConfig()\n const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)\n\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n\n if (isDisplayStatic) {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static')\n }\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getMenuElement() {\n return SelectorEngine.next(this._element, SELECTOR_MENU)[0]\n }\n\n _getPlacement() {\n const parentDropdown = this._element.parentNode\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP\n }\n\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM\n }\n\n _detectNavbar() {\n return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(val => Number.parseInt(val, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n }\n\n // Disable Popper if we have a static display\n if (this._config.display === 'static') {\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)\n\n if (!items.length) {\n return\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n\n static clearMenus(event) {\n if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {\n return\n }\n\n const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (let i = 0, len = toggles.length; i < len; i++) {\n const context = Dropdown.getInstance(toggles[i])\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n if (!context._isShown()) {\n continue\n }\n\n const relatedTarget = {\n relatedTarget: context._element\n }\n\n if (event) {\n const composedPath = event.composedPath()\n const isMenuTarget = composedPath.includes(context._menu)\n if (\n composedPath.includes(context._element) ||\n (context._config.autoClose === 'inside' && !isMenuTarget) ||\n (context._config.autoClose === 'outside' && isMenuTarget)\n ) {\n continue\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue\n }\n\n if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static getParentFromElement(element) {\n return getElementFromSelector(element) || element.parentNode\n }\n\n static dataApiKeydownHandler(event) {\n // If not input/textarea:\n // - And not a key in REGEXP_KEYDOWN => not a dropdown command\n // If input/textarea:\n // - If space key => not a dropdown command\n // - If key is other than escape\n // - If key is not up or down => not a dropdown command\n // - If trigger inside the menu => not a dropdown command\n if (/input|textarea/i.test(event.target.tagName) ?\n event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&\n ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||\n event.target.closest(SELECTOR_MENU))) :\n !REGEXP_KEYDOWN.test(event.key)) {\n return\n }\n\n const isActive = this.classList.contains(CLASS_NAME_SHOW)\n\n if (!isActive && event.key === ESCAPE_KEY) {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n\n if (isDisabled(this)) {\n return\n }\n\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (event.key === ESCAPE_KEY) {\n instance.hide()\n return\n }\n\n if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {\n if (!isActive) {\n instance.show()\n }\n\n instance._selectMenuItem(event)\n return\n }\n\n if (!isActive || event.key === SPACE_KEY) {\n Dropdown.clearMenus()\n }\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n event.preventDefault()\n Dropdown.getOrCreateInstance(this).toggle()\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Dropdown to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine'\nimport Manipulator from '../dom/manipulator'\nimport { isElement } from './index'\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width)\n }\n\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProp, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProp)\n const calculatedValue = window.getComputedStyle(element)[styleProp]\n element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, 'paddingRight')\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')\n }\n\n _saveInitialAttribute(element, styleProp) {\n const actualValue = element.style[styleProp]\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProp, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProp) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProp)\n if (typeof value === 'undefined') {\n element.style.removeProperty(styleProp)\n } else {\n Manipulator.removeDataAttribute(element, styleProp)\n element.style[styleProp] = value\n }\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n } else {\n SelectorEngine.find(selector, this._element).forEach(callBack)\n }\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { execute, executeAfterTransition, getElement, reflow, typeCheckConfig } from './index'\n\nconst Default = {\n className: 'modal-backdrop',\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n isAnimated: false,\n rootElement: 'body', // give the choice to place backdrop under different elements\n clickCallback: null\n}\n\nconst DefaultType = {\n className: 'string',\n isVisible: 'boolean',\n isAnimated: 'boolean',\n rootElement: '(element|string)',\n clickCallback: '(function|null)'\n}\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nclass Backdrop {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n if (this._config.isAnimated) {\n reflow(this._getElement())\n }\n\n this._getElement().classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n // Private\n\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n this._config.rootElement.append(this._getElement())\n\n EventHandler.on(this._getElement(), EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport SelectorEngine from '../dom/selector-engine'\nimport { typeCheckConfig } from './index'\n\nconst Default = {\n trapElement: null, // The element to trap focus inside of\n autofocus: true\n}\n\nconst DefaultType = {\n trapElement: 'element',\n autofocus: 'boolean'\n}\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nclass FocusTrap {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n activate() {\n const { trapElement, autofocus } = this._config\n\n if (this._isActive) {\n return\n }\n\n if (autofocus) {\n trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n\n _handleFocusin(event) {\n const { target } = event\n const { trapElement } = this._config\n\n if (target === document || target === trapElement || trapElement.contains(target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isRTL,\n isVisible,\n reflow,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport ScrollBarHelper from './util/scrollbar'\nimport BaseComponent from './base-component'\nimport Backdrop from './util/backdrop'\nimport FocusTrap from './util/focustrap'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n focus: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n focus: 'boolean'\n}\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._config = this._getConfig(config)\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._ignoreBackdropClick = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n\n if (this._isAnimated()) {\n this._isTransitioning = true\n }\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {\n EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => {\n if (event.target === this._element) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n const isAnimated = this._isAnimated()\n\n if (isAnimated) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n EventHandler.off(this._element, EVENT_CLICK_DISMISS)\n EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS)\n\n this._queueCallback(() => this._hideModal(), this._element, isAnimated)\n }\n\n dispose() {\n [window, this._dialog]\n .forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY))\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const isAnimated = this._isAnimated()\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n\n if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n if (isAnimated) {\n reflow(this._element)\n }\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, isAnimated)\n }\n\n _setEscapeEvent() {\n if (this._isShown) {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (this._config.keyboard && event.key === ESCAPE_KEY) {\n event.preventDefault()\n this.hide()\n } else if (!this._config.keyboard && event.key === ESCAPE_KEY) {\n this._triggerBackdropTransition()\n }\n })\n } else {\n EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())\n } else {\n EventHandler.off(window, EVENT_RESIZE)\n }\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _showBackdrop(callback) {\n EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n\n if (event.target !== event.currentTarget) {\n return\n }\n\n if (this._config.backdrop === true) {\n this.hide()\n } else if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n }\n })\n\n this._backdrop.show(callback)\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const { classList, scrollHeight, style } = this._element\n const isModalOverflowing = scrollHeight > document.documentElement.clientHeight\n\n // return if the following background transition hasn't yet completed\n if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n style.overflowY = 'hidden'\n }\n\n classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n classList.remove(CLASS_NAME_STATIC)\n if (!isModalOverflowing) {\n this._queueCallback(() => {\n style.overflowY = ''\n }, this._dialog)\n }\n }, this._dialog)\n\n this._element.focus()\n }\n\n // ----------------------------------------------------------------------\n // the following methods are used to handle overflowing modals\n // ----------------------------------------------------------------------\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) {\n this._element.style.paddingLeft = `${scrollbarWidth}px`\n }\n\n if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) {\n this._element.style.paddingRight = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](relatedTarget)\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking moddal toggler while another one is open\n const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (allReadyOpen) {\n Modal.getInstance(allReadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Modal to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isDisabled,\n isVisible,\n typeCheckConfig\n} from './util/index'\nimport ScrollBarHelper from './util/scrollbar'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\nimport SelectorEngine from './dom/selector-engine'\nimport Manipulator from './dom/manipulator'\nimport Backdrop from './util/backdrop'\nimport FocusTrap from './util/focustrap'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'offcanvas'\nconst DATA_KEY = 'bs.offcanvas'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst ESCAPE_KEY = 'Escape'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: 'boolean',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop'\nconst OPEN_SELECTOR = '.offcanvas.show'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._config = this._getConfig(config)\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._element.style.visibility = 'visible'\n\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const completeCallBack = () => {\n if (!this._config.scroll) {\n this._focustrap.activate()\n }\n\n EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })\n }\n\n this._queueCallback(completeCallBack, this._element, true)\n }\n\n hide() {\n if (!this._isShown) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._focustrap.deactivate()\n this._element.blur()\n this._isShown = false\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._element.style.visibility = 'hidden'\n\n if (!this._config.scroll) {\n new ScrollBarHelper().reset()\n }\n\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._queueCallback(completeCallback, this._element, true)\n }\n\n dispose() {\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _initializeBackDrop() {\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible: this._config.backdrop,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: () => this.hide()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (this._config.keyboard && event.key === ESCAPE_KEY) {\n this.hide()\n }\n })\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus()\n }\n })\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (allReadyOpen && allReadyOpen !== target) {\n Offcanvas.getInstance(allReadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () =>\n SelectorEngine.find(OPEN_SELECTOR).forEach(el => Offcanvas.getOrCreateInstance(el).show())\n)\n\nenableDismissTrigger(Offcanvas)\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst uriAttributes = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\n/**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i\n\n/**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[\\d+/a-z]+=*$/i\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue))\n }\n\n return true\n }\n\n const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)\n\n // Check if a regular expression validates the attribute.\n for (let i = 0, len = regExp.length; i < len; i++) {\n if (regExp[i].test(attributeName)) {\n return true\n }\n }\n\n return false\n}\n\nexport const DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFn && typeof sanitizeFn === 'function') {\n return sanitizeFn(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (let i = 0, len = elements.length; i < len; i++) {\n const element = elements[i]\n const elementName = element.nodeName.toLowerCase()\n\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove()\n\n continue\n }\n\n const attributeList = [].concat(...element.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])\n\n attributeList.forEach(attribute => {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName)\n }\n })\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\n\nimport {\n defineJQueryPlugin,\n findShadowRoot,\n getElement,\n getUID,\n isElement,\n isRTL,\n noop,\n typeCheckConfig\n} from './util/index'\nimport { DefaultAllowlist, sanitizeHtml } from './util/sanitizer'\nimport Data from './dom/data'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'tooltip'\nconst DATA_KEY = 'bs.tooltip'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst CLASS_PREFIX = 'bs-tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst DefaultType = {\n animation: 'boolean',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string',\n delay: '(number|object)',\n html: 'boolean',\n selector: '(string|boolean)',\n placement: '(string|function)',\n offset: '(array|string|function)',\n container: '(string|element|boolean)',\n fallbackPlacements: 'array',\n boundary: '(string|element)',\n customClass: '(string|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n allowList: 'object',\n popperConfig: '(null|object|function)'\n}\n\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n}\n\nconst Default = {\n animation: true,\n template: '
' +\n '
' +\n '
' +\n '
',\n trigger: 'hover focus',\n title: '',\n delay: 0,\n html: false,\n selector: false,\n placement: 'top',\n offset: [0, 0],\n container: false,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n boundary: 'clippingParents',\n customClass: '',\n sanitize: true,\n sanitizeFn: null,\n allowList: DefaultAllowlist,\n popperConfig: null\n}\n\nconst Event = {\n HIDE: `hide${EVENT_KEY}`,\n HIDDEN: `hidden${EVENT_KEY}`,\n SHOW: `show${EVENT_KEY}`,\n SHOWN: `shown${EVENT_KEY}`,\n INSERTED: `inserted${EVENT_KEY}`,\n CLICK: `click${EVENT_KEY}`,\n FOCUSIN: `focusin${EVENT_KEY}`,\n FOCUSOUT: `focusout${EVENT_KEY}`,\n MOUSEENTER: `mouseenter${EVENT_KEY}`,\n MOUSELEAVE: `mouseleave${EVENT_KEY}`\n}\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst HOVER_STATE_SHOW = 'show'\nconst HOVER_STATE_OUT = 'out'\n\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner'\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`\n\nconst EVENT_MODAL_HIDE = 'hide.bs.modal'\n\nconst TRIGGER_HOVER = 'hover'\nconst TRIGGER_FOCUS = 'focus'\nconst TRIGGER_CLICK = 'click'\nconst TRIGGER_MANUAL = 'manual'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)')\n }\n\n super(element)\n\n // private\n this._isEnabled = true\n this._timeout = 0\n this._hoverState = ''\n this._activeTrigger = {}\n this._popper = null\n\n // Protected\n this._config = this._getConfig(config)\n this.tip = null\n\n this._setListeners()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get Event() {\n return Event\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle(event) {\n if (!this._isEnabled) {\n return\n }\n\n if (event) {\n const context = this._initializeOnDelegatedTarget(event)\n\n context._activeTrigger.click = !context._activeTrigger.click\n\n if (context._isWithActiveTrigger()) {\n context._enter(null, context)\n } else {\n context._leave(null, context)\n }\n } else {\n if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {\n this._leave(null, this)\n return\n }\n\n this._enter(null, this)\n }\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this.tip) {\n this.tip.remove()\n }\n\n this._disposePopper()\n super.dispose()\n }\n\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n if (!(this.isWithContent() && this._isEnabled)) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, this.constructor.Event.SHOW)\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = shadowRoot === null ?\n this._element.ownerDocument.documentElement.contains(this._element) :\n shadowRoot.contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`\n // This will be removed later in favor of a `setContent` method\n if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {\n this._disposePopper()\n this.tip.remove()\n this.tip = null\n }\n\n const tip = this.getTipElement()\n const tipId = getUID(this.constructor.NAME)\n\n tip.setAttribute('id', tipId)\n this._element.setAttribute('aria-describedby', tipId)\n\n if (this._config.animation) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n const placement = typeof this._config.placement === 'function' ?\n this._config.placement.call(this, tip, this._element) :\n this._config.placement\n\n const attachment = this._getAttachment(placement)\n this._addAttachmentClass(attachment)\n\n const { container } = this._config\n Data.set(tip, this.constructor.DATA_KEY, this)\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.Event.INSERTED)\n }\n\n if (this._popper) {\n this._popper.update()\n } else {\n this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n const customClass = this._resolvePossibleFunction(this._config.customClass)\n if (customClass) {\n tip.classList.add(...customClass.split(' '))\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n [].concat(...document.body.children).forEach(element => {\n EventHandler.on(element, 'mouseover', noop)\n })\n }\n\n const complete = () => {\n const prevHoverState = this._hoverState\n\n this._hoverState = null\n EventHandler.trigger(this._element, this.constructor.Event.SHOWN)\n\n if (prevHoverState === HOVER_STATE_OUT) {\n this._leave(null, this)\n }\n }\n\n const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(complete, this.tip, isAnimated)\n }\n\n hide() {\n if (!this._popper) {\n return\n }\n\n const tip = this.getTipElement()\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (this._hoverState !== HOVER_STATE_SHOW) {\n tip.remove()\n }\n\n this._cleanTipClass()\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)\n\n this._disposePopper()\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n tip.classList.remove(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n [].concat(...document.body.children)\n .forEach(element => EventHandler.off(element, 'mouseover', noop))\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n\n const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(complete, this.tip, isAnimated)\n this._hoverState = ''\n }\n\n update() {\n if (this._popper !== null) {\n this._popper.update()\n }\n }\n\n // Protected\n\n isWithContent() {\n return Boolean(this.getTitle())\n }\n\n getTipElement() {\n if (this.tip) {\n return this.tip\n }\n\n const element = document.createElement('div')\n element.innerHTML = this._config.template\n\n const tip = element.children[0]\n this.setContent(tip)\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n\n this.tip = tip\n return this.tip\n }\n\n setContent(tip) {\n this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER)\n }\n\n _sanitizeAndSetContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!content && templateElement) {\n templateElement.remove()\n return\n }\n\n // we use append for html objects to maintain js events\n this.setElementContent(templateElement, content)\n }\n\n setElementContent(element, content) {\n if (element === null) {\n return\n }\n\n if (isElement(content)) {\n content = getElement(content)\n\n // content is a DOM node or a jQuery\n if (this._config.html) {\n if (content.parentNode !== element) {\n element.innerHTML = ''\n element.append(content)\n }\n } else {\n element.textContent = content.textContent\n }\n\n return\n }\n\n if (this._config.html) {\n if (this._config.sanitize) {\n content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn)\n }\n\n element.innerHTML = content\n } else {\n element.textContent = content\n }\n }\n\n getTitle() {\n const title = this._element.getAttribute('data-bs-original-title') || this._config.title\n\n return this._resolvePossibleFunction(title)\n }\n\n updateAttachment(attachment) {\n if (attachment === 'right') {\n return 'end'\n }\n\n if (attachment === 'left') {\n return 'start'\n }\n\n return attachment\n }\n\n // Private\n\n _initializeOnDelegatedTarget(event, context) {\n return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(val => Number.parseInt(val, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(content) {\n return typeof content === 'function' ? content.call(this._element) : content\n }\n\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [\n {\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n },\n {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n },\n {\n name: 'onChange',\n enabled: true,\n phase: 'afterWrite',\n fn: data => this._handlePopperPlacementChange(data)\n }\n ],\n onFirstUpdate: data => {\n if (data.options.placement !== data.placement) {\n this._handlePopperPlacementChange(data)\n }\n }\n }\n\n return {\n ...defaultBsPopperConfig,\n ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n }\n }\n\n _addAttachmentClass(attachment) {\n this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`)\n }\n\n _getAttachment(placement) {\n return AttachmentMap[placement.toUpperCase()]\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n triggers.forEach(trigger => {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event))\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.Event.MOUSEENTER :\n this.constructor.Event.FOCUSIN\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.Event.MOUSELEAVE :\n this.constructor.Event.FOCUSOUT\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => this._enter(event))\n EventHandler.on(this._element, eventOut, this._config.selector, event => this._leave(event))\n }\n })\n\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide()\n }\n }\n\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this._config.selector) {\n this._config = {\n ...this._config,\n trigger: 'manual',\n selector: ''\n }\n } else {\n this._fixTitle()\n }\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n const originalTitleType = typeof this._element.getAttribute('data-bs-original-title')\n\n if (title || originalTitleType !== 'string') {\n this._element.setAttribute('data-bs-original-title', title || '')\n if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('title', '')\n }\n }\n\n _enter(event, context) {\n context = this._initializeOnDelegatedTarget(event, context)\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER\n ] = true\n }\n\n if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {\n context._hoverState = HOVER_STATE_SHOW\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HOVER_STATE_SHOW\n\n if (!context._config.delay || !context._config.delay.show) {\n context.show()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HOVER_STATE_SHOW) {\n context.show()\n }\n }, context._config.delay.show)\n }\n\n _leave(event, context) {\n context = this._initializeOnDelegatedTarget(event, context)\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER\n ] = context._element.contains(event.relatedTarget)\n }\n\n if (context._isWithActiveTrigger()) {\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HOVER_STATE_OUT\n\n if (!context._config.delay || !context._config.delay.hide) {\n context.hide()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HOVER_STATE_OUT) {\n context.hide()\n }\n }, context._config.delay.hide)\n }\n\n _isWithActiveTrigger() {\n for (const trigger in this._activeTrigger) {\n if (this._activeTrigger[trigger]) {\n return true\n }\n }\n\n return false\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n Object.keys(dataAttributes).forEach(dataAttr => {\n if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {\n delete dataAttributes[dataAttr]\n }\n })\n\n config = {\n ...this.constructor.Default,\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n\n config.container = config.container === false ? document.body : getElement(config.container)\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n typeCheckConfig(NAME, config, this.constructor.DefaultType)\n\n if (config.sanitize) {\n config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn)\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const key in this._config) {\n if (this.constructor.Default[key] !== this._config[key]) {\n config[key] = this._config[key]\n }\n }\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config\n }\n\n _cleanTipClass() {\n const tip = this.getTipElement()\n const basicClassPrefixRegex = new RegExp(`(^|\\\\s)${this._getBasicClassPrefix()}\\\\S+`, 'g')\n const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex)\n if (tabClass !== null && tabClass.length > 0) {\n tabClass.map(token => token.trim())\n .forEach(tClass => tip.classList.remove(tClass))\n }\n }\n\n _getBasicClassPrefix() {\n return CLASS_PREFIX\n }\n\n _handlePopperPlacementChange(popperData) {\n const { state } = popperData\n\n if (!state) {\n return\n }\n\n this.tip = state.elements.popper\n this._cleanTipClass()\n this._addAttachmentClass(this._getAttachment(state.placement))\n }\n\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Tooltip to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport Tooltip from './tooltip'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'popover'\nconst DATA_KEY = 'bs.popover'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst CLASS_PREFIX = 'bs-popover'\n\nconst Default = {\n ...Tooltip.Default,\n placement: 'right',\n offset: [0, 8],\n trigger: 'click',\n content: '',\n template: '
' +\n '
' +\n '

' +\n '
' +\n '
'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(string|element|function)'\n}\n\nconst Event = {\n HIDE: `hide${EVENT_KEY}`,\n HIDDEN: `hidden${EVENT_KEY}`,\n SHOW: `show${EVENT_KEY}`,\n SHOWN: `shown${EVENT_KEY}`,\n INSERTED: `inserted${EVENT_KEY}`,\n CLICK: `click${EVENT_KEY}`,\n FOCUSIN: `focusin${EVENT_KEY}`,\n FOCUSOUT: `focusout${EVENT_KEY}`,\n MOUSEENTER: `mouseenter${EVENT_KEY}`,\n MOUSELEAVE: `mouseleave${EVENT_KEY}`\n}\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Popover extends Tooltip {\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get Event() {\n return Event\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Overrides\n\n isWithContent() {\n return this.getTitle() || this._getContent()\n }\n\n setContent(tip) {\n this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TITLE)\n this._sanitizeAndSetContent(tip, this._getContent(), SELECTOR_CONTENT)\n }\n\n // Private\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n _getBasicClassPrefix() {\n return CLASS_PREFIX\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Popover to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElement,\n getSelectorFromElement,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst Default = {\n offset: 10,\n method: 'auto',\n target: ''\n}\n\nconst DefaultType = {\n offset: 'number',\n method: 'string',\n target: '(string|element)'\n}\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_SCROLL = `scroll${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_NAV_LINKS = '.nav-link'\nconst SELECTOR_NAV_ITEMS = '.nav-item'\nconst SELECTOR_LIST_ITEMS = '.list-group-item'\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}, .${CLASS_NAME_DROPDOWN_ITEM}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst METHOD_OFFSET = 'offset'\nconst METHOD_POSITION = 'position'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element)\n this._scrollElement = this._element.tagName === 'BODY' ? window : this._element\n this._config = this._getConfig(config)\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n EventHandler.on(this._scrollElement, EVENT_SCROLL, () => this._process())\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window ?\n METHOD_OFFSET :\n METHOD_POSITION\n\n const offsetMethod = this._config.method === 'auto' ?\n autoMethod :\n this._config.method\n\n const offsetBase = offsetMethod === METHOD_POSITION ?\n this._getScrollTop() :\n 0\n\n this._offsets = []\n this._targets = []\n this._scrollHeight = this._getScrollHeight()\n\n const targets = SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target)\n\n targets.map(element => {\n const targetSelector = getSelectorFromElement(element)\n const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n return [\n Manipulator[offsetMethod](target).top + offsetBase,\n targetSelector\n ]\n }\n }\n\n return null\n })\n .filter(item => item)\n .sort((a, b) => a[0] - b[0])\n .forEach(item => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n EventHandler.off(this._scrollElement, EVENT_KEY)\n super.dispose()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' && config ? config : {})\n }\n\n config.target = getElement(config.target) || document.documentElement\n\n typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window ?\n this._scrollElement.pageYOffset :\n this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window ?\n window.innerHeight :\n this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n for (let i = this._offsets.length; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n const queries = SELECTOR_LINK_ITEMS.split(',')\n .map(selector => `${selector}[data-bs-target=\"${target}\"],${selector}[href=\"${target}\"]`)\n\n const link = SelectorEngine.findOne(queries.join(','), this._config.target)\n\n link.classList.add(CLASS_NAME_ACTIVE)\n if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n } else {\n SelectorEngine.parents(link, SELECTOR_NAV_LIST_GROUP)\n .forEach(listGroup => {\n // Set triggered links parents as active\n // With both
    and