Last updated: 2026-09-26
From v0.4.0, users now have the option to Save and Load Sessions (located on the Main Simulation page, below the code editor), which may be more convenient. The original password-unlocked models functionality detailed below still remains supported, as it can handle multiple models.
Somewhat related but important to note that from v0.4.5, users now can use the Download Model button (located on the Main Simulation page, below the code editor) to export a standalone .zip file containing all necessary information to fully reproduce the simulations from MVP.
To avoid re-entering in model code, dosing regimens, and other settings every time, users can provide an additional file to be sourced by MVP during launch. To use this functionality, it is assumed that users have a working knowledge of the Shiny framework.
A list of models (stored as character objects) are included by
default when MVPapp is installed. This is the list that the
users sees in the drop-down list when they select models, and is stored
as a vector of characters called model_examples_list. There
are two functions related to model selection:
model_switch_conditions() pastes the relevant model
code into the code editor within the App when the user selects a
matching model title from the drop-down list.update_model_choices() stores model-specific
information such as dosing regimens, MW, axis labels and scaling
(i.e. any Shiny Inputs) associated with a password.When both of these functions are re-defined by an external R script, additional password-protected models become available in MVP.
The external script is evaluated inside the app’s own environment, so
the internal objects it refers to
(e.g. model_examples_list, code_preamble,
code_postamble and create_alert()) are
available directly by name, even though they are not exported from
MVPapp. Outside the app they can be inspected with the
::: operator,
e.g. MVPapp:::model_examples_list.
An example external file is provided in
/shiny/passworded_models_example.R. The user is recommended
to copy this script locally and use it as a starting point to insert
models, and supply the path for the pw_models_path argument
to run_mvp(),
i.e. run_mvp(pw_models_path = "path/to/your/private/models.R").
model_switch_conditions()The structure of model_switch_conditions() is relatively
straightforward - each model is provided as a key-value pair, where the
left-hand side (LHS) is the title of the model, and the right-hand side
(RHS) is the code to be pasted when the associated title is
selected.
mcode_model_choice). The postamble code is
required to tell apart Model 1 / Model 2 during model compilation.In the example (passworded_models_example.R), you can
see that the first one on the list is called “Test Passworded Model”.
Note that this model is not visible when you launch MVP - this behavior
is intended, as “Test Passworded Model” is hidden from selection by
default from the drop-down list until a valid password has been supplied
from the “More…” tab (in this case the password is “test”), in which
case the model is “unlocked” and become available for selection. This is
the basis of how passworded models work in MVP.
Therefore, the list in model_switch_conditions() must contain every model, both public and private.
update_model_choices()The structure of update_model_choices() looks a bit more
complicated, however none of the arguments nor its return value is of
importance for the user. The only relevant part is the following:
list_of_valid_passwords <- c("test")
### Example model
if (input_password == "test") {
create_alert("FirstName LastName", "[email protected]")
new_choices <- c('Test Passworded Model', model_examples_list)
updateXXXInput(session, "YYY", value = ZZZ)
.
.
.
}
list_of_valid_passwords should be edited to contain a
vector of characters that are valid passwords.(input_password == "test") should be edited to contain
password(s) associated with the model(s) that the user wishes to be
unlocked.create_alert() optional, this creates a pop-up
notifying the user that the model is now unlocked (requires
shinyalert package). The first name, last name and email
should be changed accordingly.new_choices should be edited to insert the
matching title(s) associated with the model(s), and
added to the original model_examples_list.XXX is the type of Input, "YYY" is the id name
associated with the Input, and ZZZ is the new default
value.
updateNumericInput(session, "amt1", value = 1000).⚠️ If your external script was copied from a version of MVP before
1.0.0, please replace the block after
# Update the selectInput with the new choices (at the end
of update_model_choices()) with the one in the current
passworded_models_example.R. Older copies reset both model
selections (and any edited model code) while typing in the password box
(#36).
As almost all Shiny Inputs can be updated this way and there are far
too many to list, the most common Input id names are
included below for reference:
| Description | Shiny Input Type | id |
|---|---|---|
| BLQ filter | checkboxInput | “BLQ_filter” |
| EVID filter | checkboxInput | “EVID_filter” |
| Coerce into numeric | checkboxInput | “turn_all_numeric” |
| Data Filtering Code Editor | shinyAce::updateAceEditor | “codes” |
shinyAce::updateAceEditor(session, "codes", value = "filter(VISITN <= 52) %>% rename(DV = FVC)" )| Description | Shiny Input Type | id |
|---|---|---|
| Dose Amount, Regimen 1 (Model 1) | updateNumericInput | “amt1” |
| First Dose Time, Regimen 1 (Model 1) | updateNumericInput | “delay_time1” |
| Total Doses, Regimen 1 (Model 1) | updateNumericInput | “total1” |
| Dosing Interval, Regimen 1 (Model 1) | updateNumericInput | “ii1” |
| Infusion Duration, Regimen 1 (Model 1) | updateNumericInput | “tinf1” |
| Use MW (Model 1) | updateCheckboxInput | “mw_checkbox” |
| MW value (Model 1) | updateNumericInput | “mw” |
| MW multiplication factor (Model 1) | updateNumericInput | “multi_factor” |
| Use Weight-based dosing (Model 1) | updateCheckboxInput | “wt_based_dosing_checkbox” |
| Weight-based parameter name (Model 1) | updateTextInput | “wt_based_dosing_name” |
| Model Duration (Model 1) | updateCheckboxInput | “model_dur_checkbox” |
| Model Rate (Model 1) | updateCheckboxInput | “model_rate_checkbox” |
"amt5" to update
the dose amount for regimen 5._2 as a suffix to the id name. E.g.
total doses for model 2, regimen 3, would be
"total3_2"| Description | Shiny Input Type | id |
|---|---|---|
| Max Sampling Time | updateNumericInput | “tgrid_max” |
| Sampling Frequency | updateNumericInput | “delta” |
| Custom Sampling Schedule | updateTextInput | “custom_sampling_time_text” |
| Add Sampling at Time 0 | updateCheckboxInput | “add_time_zero” |
update_model_choices() function) as an
object and then referenced accordingly, e.g.:q1w_sampling <- c(2, 4, 8, 24, 72, 168)
q3w_sampling <- c(2, 4, 8, 24, 72, 168, 336, 504) + 168*6
q1w_repeated <- unlist(lapply(0:5, function(x) q1w_sampling + x * 168)) # end of Q1W sampling
q3w_repeated <- unlist(lapply(0:7, function(x) q3w_sampling + x * 504)) # end of Q3W sampling
updateTextInput(session, "custom_sampling_time_text", value = 'c(q1w_repeated, q3w_repeated)')| Description | Shiny Input Type | id |
|---|---|---|
| Y-axis label | updateTextInput | “y_axis_label” |
| Log Y-axis (Simulation Page) | updateCheckboxInput | “log_y_axis” |
| Log Y-axis (PSA Page, Model 1) | updateCheckboxInput | “log_y_axis_model_1” |
| Log Y-axis (PSA Page, Model 2) | updateCheckboxInput | “log_y_axis_model_2” |
| Log Y-axis (Variability Page) | updateCheckboxInput | “log_y_axis_iiv” |
| Scale X-axis | updateSelectizeInput | “time_unit” |
| Show Sampling Points [sims] | updateCheckboxInput | “geom_point_sim_option” |
x_axis.| Description | Shiny Input Type | id | e.g. | |
|---|---|---|---|---|
| Select Patient Database, Model 1 | updateSelectizeInput | “db_model_1” | selected = “NHANES” | |
| N Subjects, Model 1 | updateNumericInput | “n_subj_model_1” | ||
| Seed, Model 1 | updateNumericInput | “seed_number_model_1” | ||
| Age, Model 1 | updateSliderInput | “age_db_model_1” | min = 0, max = 100, value = c(18, 65) | |
| Weight, Model 1 | updateSliderInput | “wt_db_model_1” | min = 0, max = 150, value = c(0, 150) | |
| Males Percentage, Model 1 | updateSliderInput | “males_db_model_1” | min = 0, max = 100, value = 100, step = 5 | |
| Custom Covariate 1, Model 1 | updateTextInput | “custom_cov_1_model_1” | value = “CMINSS” | |
| Custom Covariate 1 Distribution, Model 1 | updateSelectInput | “custom_cov_1_dist_model_1” | selected = “Log-Normal” | |
| Y-value Threshold | updateNumericInput | “y_value_threshold” | value = 0 |
model_2 to
update Inputs for Model 2.If the user wishes to update other Inputs that are not listed above,
please refer to the source code for shiny/app.R.
Note: If the additional model(s) do not need to be
password-gated, the entire update_model_choices() function
can be removed from the external script (i.e. do not need to be
re-defined), and only model_switch_conditions() needs to be
changed to include the additional models.
The last piece to tie it all together is to supply the actual model code itself. MVP accepts the character form of mrgsolve code, so objects have to be defined in the format of the following:
model_name refers to the user-defined name for the
model, i.e. the R object that will be used on the RHS to match the
corresponding LHS title in model_switch_conditions().code_premable and code_postamble are
boilerplate code to remind users the model should be in character form
surrounded by double quotation marks, and to assign it to the
model_object within the code editor. This should
always be included unchanged."). All
parameters defined under $PARAM will be available on the Simulation page
to be adjusted in real-time. OMEGAs and SIGMAs values should still be
supplied, as they can be adjusted on the Variability page (note: all
sources of variability are ignored by zero_re() on the
Simulation page). The user should pay special attention to
remove both single and double quotation marks, or escape it by
using the back slash if it exists in the original code, as that
can be a frequent source of error.The most ideal process would be to allow saving & opening projects based on a password all within the Shiny UI itself. However, since MVP was not originally developed with a database architecture in mind, the present solution still provides an an acceptable alternative to allow additional models to be supplied externally upon App launch. The main advantage of this approach is that if MVP is deployed and hosted elsewhere (e.g. via Posit Connect), as long as the hosted environment has access to the external models script, the App does not need to be re-deployed to access the updated models.