Skip to contents
library(Certara.RDarwin)
# for tidy style
library(magrittr)
#> Warning: package 'magrittr' was built under R version 4.3.3

Introduction

This vignette provides an illustrative workflow for using the Certara.RDarwin package, a toolkit designed to facilitate the usage of pyDarwin with the Certara NLME pharmacometric modeling engine from the R command line. This particular example demonstrates the step-by-step process of creating, configuring, and executing a model search using RDarwin in combination with the pyDarwin framework.

Data

The data set used in this example (pkOralBolus.csv) is made available in the package examples folder, however, we will copy them to our current working directory for ease of access. We will also need to copy an R script to be used for postprocessing.

WorkingDir <- tempdir()
DataFilePath <- file.path(WorkingDir, "pkOralBolus.csv")
file.copy(system.file(package = "Certara.RDarwin", "examples", "pkOralBolus.csv"),
          DataFilePath, overwrite = TRUE)
RScriptPath <- file.path(WorkingDir, "penaltyDiffObservedSimulatedCmax.R")
file.copy(system.file(package = "Certara.RDarwin", "examples", "penaltyDiffObservedSimulatedCmax.R"),
          RScriptPath, overwrite = TRUE)

Setting Up the Model

The first section of the workflow involves setting up the model. In this example, we will configure a first-order absorption model with parameterization set to “Clearance.” We specify several model characteristics, such as the number of compartments, absorption type, and parameterization. Additionally, we introduce parameters to the model and make modifications to their initial estimates. In this workflow, the following actions are taken:

  1. Setting Up the Model: We use the create_ModelPK() function to configure the model’s structures, including the number of compartments, absorption type, and parameterization.
modelPMLCodes <-
  create_ModelPK(
    CompartmentsNumber = c(1, 2),
    Absorption = c("First-Order"),
    Parameterization = "Clearance"
  )
  1. Introducing Parameters: Next, we introduce a parameter named “F” to the model. This parameter is associated with a LogitNormal distribution, the initial estimates of its corresponding fixed effect is set to -1, and its associated random effect is set to “Searched.” This step is accomplished using the add_StParm function.
modelPMLCodes <-
  add_StParm(
    modelPMLCodes,
    StParmName = "F",
    Type = "LogitNormal",
    State = "Searched",
    ThetaStParm = Theta(Name = "tvlogitF", InitialEstimates = -1),
    OmegaStParm = Omega(Name = "nlogitF", State = "Searched"),
    DosepointArgName = "bioavail"
  )
  1. Modifying Initial Estimates: We modify the initial estimates for a theta parameter (“tvV”) and an omega parameter (“nCl”) using the modify_Theta and modify_Omega functions, respectively.
# list Thetas available
list_Thetas(modelPMLCodes)
#> [1] "tvCl"     "tvV"      "tvKa"     "tvlogitF" "tvCl2"    "tvV2"

# list Omegas available
list_Omegas(modelPMLCodes)
#> [1] "nCl"     "nV"      "nKa"     "nlogitF" "nCl2"    "nV2"

# change initial estimates for Theta and Omega
modelPMLCodes <-
  modify_Theta(
    modelPMLCodes,
    Name = "tvV",
    InitialEstimates = c(0, 5, 1000)
  ) %>%
  modify_Omega(
    Name = "nCl",
    InitialOmega = 5
  )
  1. Residual Error Model Configuration: In this example, we disable the proportional error model (which is enabled by default) and enable an Additive+ Multiplicative error model using the modify_Observation function.
# list observations available
list_Observations(modelPMLCodes)
#> [1] "CObs"

modelPMLCodes <-
  modelPMLCodes |>
  modify_Observation(
    ObservationName = "CObs",
    SigmasChosen =
      Sigmas(
        Proportional = 0,
        AdditiveMultiplicative = list(PropPart = 0.1, AddPart = 10)
      )
  )
  1. Search Zero-Order Absorption: We expand the search space by adding zero-order absorption on one-compartment models.
# add zero order absorption to one compartment model
modelPMLCodes <-
  modelPMLCodes |>
  add_StParm(
    StParmName = "Rate",
    Type = "LogNormal2",
    State = "Searched",
    ThetaStParm = Theta(Name = "tvlogRate", InitialEstimates = 0),
    OmegaStParm = Omega(Name = "nlogRate", State = "Present"),
    DosepointArgName = "rate",
    PMLStructures = c("PK1FOC")
  )

# see the resulted template
print(modelPMLCodes)
#> PK1FOC 
#>  test() {
#>  cfMicro(A1, Cl / V, first = (Aa = Ka))
#>  C = A1 / V
#>  dosepoint(Aa{_F[1]}{_Rate[1]}, idosevar = AaDose, infdosevar = AaInfDose, infratevar = AaInfRate)
#>  {_F[2]}
#>  {_Rate[2]}
#>  error(CEps = 10)
#>  observe(CObs = C + CEps*sqrt(1 + C*C*(CMultStdev/sigma())^2))
#>  stparm(CMultStdev = tvCMultStdev )
#>  fixef(tvCMultStdev= c(, 0.1, ))
#>  
#>  
#>  stparm(Cl = tvCl * exp( nCl ))
#>  fixef(tvCl= c(, 1, ))
#>  ranef(diag(nCl) = c(5))
#>  stparm(V = tvV * exp( nV ))
#>  fixef(tvV= c(0, 5, 1000))
#>  ranef(diag(nV) = c(1))
#>  stparm(Ka = tvKa * exp( nKa ))
#>  fixef(tvKa= c(, 1, ))
#>  ranef(diag(nKa) = c(1))
#> 
#> } 
#> PK2FOC 
#>  test() {
#>  cfMicro(A1, Cl / V, Cl2 / V, Cl2 / V2, first = (Aa = Ka))
#> C = A1 / V
#>  dosepoint(Aa{_F[1]}, idosevar = AaDose, infdosevar = AaInfDose, infratevar = AaInfRate)
#>  {_F[2]}
#>  error(CEps = 10)
#>  observe(CObs = C + CEps*sqrt(1 + C*C*(CMultStdev/sigma())^2))
#>  stparm(CMultStdev = tvCMultStdev )
#>  fixef(tvCMultStdev= c(, 0.1, ))
#>  
#>  
#>  stparm(Cl = tvCl * exp( nCl ))
#>  fixef(tvCl= c(, 1, ))
#>  ranef(diag(nCl) = c(5))
#>  stparm(V = tvV * exp( nV ))
#>  fixef(tvV= c(0, 5, 1000))
#>  ranef(diag(nV) = c(1))
#>  stparm(Cl2 = tvCl2 * exp( nCl2 ))
#>  fixef(tvCl2= c(, 1, ))
#>  ranef(diag(nCl2) = c(1))
#>  stparm(V2 = tvV2 * exp( nV2 ))
#>  fixef(tvV2= c(, 1, ))
#>  ranef(diag(nV2) = c(1))
#>  stparm(Ka = tvKa * exp( nKa ))
#>  fixef(tvKa= c(, 1, ))
#>  ranef(diag(nKa) = c(1))
#> 
#> }

Engine and Simulation Setups

The next step in our workflow involves setting up the engine and simulation parameters. In this example, we use the FOCE-ELS (First-Order Conditional Estimation with Interaction) method for optimization and specify simulation settings. These are the actions performed:

  1. Engine Setup: We use the specify_EngineParams function to set up the engine parameters. In this case, we choose the FOCE-ELS method, configure standard error estimation, and set the number of iterations for Maximum A Posterior (MAP) initial Naive Pooling optimization.
engine_Setup <- specify_EngineParams(
  method = "FOCE-ELS",
  stdErr = "None",
  numIterMAPNP = 3
)
  1. Simulation Setup: We specify the simulation parameters using the specify_SimParams function. In this example, we configure the number of replicates and set a seed for reproducibility.
Sim_Setup <- specify_SimParams(
  numReplicates = 10,
  seed = 12345678
)
  1. Specifying Output Tables: The tables are specified using the Table() function. In this example, we introduce two output tables. The first table, “ObsData.csv” is generated after estimation step to get observations (CObs) from the source file. The second table, “SimData.csv”, is generated during simulation (by setting the argument ForSimulation = TRUE) and includes simulated data for CObs.
# specify table to output after optimization
simple_table01 <-
  Table(
    Name = "ObsData.csv",
    KeepSource = TRUE,
    WhenObs = c("CObs")
  )

# specify table to output after simulation
Sim_table01 <-
  Table(
    Name = "SimData.csv",
    KeepSource = TRUE,
    VariablesList = c("CObs"),
    ForSimulation = TRUE
  )

Generating Template and Token Files

Once the model and parameter setups are complete, we use the write_ModelTemplateTokens function to generate the template (template.txt) and token (tokens.json) files, which define the model structure, its characteristics, and data mappings - all of which are essential to the model search. This function also allows the specification of omega structures to be searched through the argument OmegaSearchBlock.

# Generate template and token files
ModelTemplateTokens <-
  write_ModelTemplateTokens(
    TemplateFilePath = file.path(WorkingDir, "template.txt"),
    TokensFilePath = file.path(WorkingDir, "tokens.json"),
    Description = "search--123F-12Tlag-1Rate",
    Author = "Certara",
    DataFilePath = DataFilePath,
    DataMapping = c(
      id = "ID",
      time = "time",
      AMT = "Dose",
      CObs = "CObs"
    ),
    PMLParametersSets = modelPMLCodes,
    EstArgs = engine_Setup,
    SimArgs = Sim_Setup,
    Tables = list(simple_table01, Sim_table01)
  )

Generating Options File

The next step in the workflow is the generation of the options file, options.json. This file is crucial for configuring the pyDarwin project according to specific requirements. The following actions are taken:

We configure the options for the pyDarwin project using the create_pyDarwinOptions function. This includes specifying the algorithm, timeouts, directories, and post-processing settings. In this example, we use post run R code, which is specified through the postprocess argument.

It is assumed to have NLME Engine installed/unzipped and INSTALLDIR environment variable pointed to the installation directory (C:/Program Files/Certara/NLME_Engine on Windows). Please modify it accordingly.

# relative paths are used for convenience
data_dir <- "../../../.."
working_dir <- "{project_dir}/Results"
output_dir <- "{project_dir}/Results/output"
temp_dir <- "{project_dir}/Results/temp"

optionSetup <- create_pyDarwinOptions(
  algorithm = "GA",
  model_run_timeout = 2400,
  data_dir = data_dir,
  working_dir = working_dir,
  output_dir = output_dir,
  temp_dir = temp_dir,
  nlme_dir = Sys.getenv("INSTALLDIR"),
  remove_temp_dir = FALSE,
  postprocess =
    pyDarwinOptionsPostprocess(
      use_r = TRUE,
      post_run_r_code = RScriptPath
    )
)

We use the write_pyDarwinOptions function to generate the options.json file based on the setup created in the previous step.

write_pyDarwinOptions(pyDarwinOptions = optionSetup,
                      file = file.path(WorkingDir, "options.json"))

Postprocessing R Code Requirements

When using R code for postprocessing with pyDarwin, the R script must return a character vector of length 2, structured as follows:

  • Penalty Value: The first element of the vector is a string that can be interpreted as a numeric value, representing the penalty. This value is used directly in the pyDarwin model selection process, contributing to the fitness/reward value of a particular model configuration. The penalty can be any real number, including negative values, but is typically positive. This penalty influences the selection of subsequent populations during the evolutionary search.

  • Comment for Documentation: The second element of the returned vector is a string that is appended as a comment to the output file generated by pyDarwin. This comment is meant to document the output of the R code but has no effect on the pyDarwin process itself. The content of this element is entirely up to the user and can be used to provide additional context or information about the results of the postprocessing step.

The content of the second element (the comment) has no impact on pyDarwin’s model search, while the penalty value plays a significant role in guiding the evolutionary search process.

Running pyDarwin

With the template, token, and options files in place, we are ready to execute the pyDarwin model search. In this final step, we run the search using the run_pyDarwin function. The following actions are performed:

  1. Specify Python Path: We specify the path to the Python interpreter executable. Please follow pyDarwin installation guide for instructions. Current row should be modified accordingly.
pythonPath <- "./venv/Scripts/python.exe"
  1. Run pyDarwin: We launch the pyDarwin search process using the run_pyDarwin function. This function monitors the search progress and, depending on the configuration, either waits for the search to complete or exits immediately.
job <- run_pyDarwin(InterpreterPath = pythonPath,
                    DirectoryPath = WorkingDir,
                    TemplatePath = "template.txt",
                    TokensPath = "tokens.json",
                    OptionsPath = "options.json",
                    Wait = TRUE)
 Running pyDarwin v3.0.0
 Options file found at /tmp/RtmplOWzal/options.json
 Preparing project working folder...
 Preparing project output folder...
 Preparing project temp folder...
 Using darwin.MemoryModelCache
 Writing intermediate output to /tmp/RtmplOWzal/Results/output/results.csv
 Models will be saved in /tmp/RtmplOWzal/Results/models.json
 Template file found at /tmp/RtmplOWzal/template.txt
 Tokens file found at /tmp/RtmplOWzal/tokens.json
 Algorithm: GA
 Engine: NLME
 random_seed: 11
 Project dir: /tmp/RtmplOWzal
 Data dir: ../../../..
 Project working dir: /tmp/RtmplOWzal/Results
 Project temp dir: /tmp/RtmplOWzal/Results/temp
 Project output dir: /tmp/RtmplOWzal/Results/output
 Key models dir: /tmp/RtmplOWzal/Results/key_models
 Search space size: 16
 Estimated number of models to run: 264
 NLME found: /home/user/Desktop/installdir/
 GCC found: /usr
 Search start time: Wed Nov  1 15:59:04 2023
 Starting generation 1
 RScript found at /usr/lib/R/bin/Rscript
 Post Run R code found at /tmp/RtmplOWzal/penaltyDiffObservedSimulatedCmax.R
 Not using Post Run Python code
 Checking files in /tmp/RtmplOWzal/Results/temp/1/1
 Generation =     1, Model     2,           Done,    fitness =  7570.350,    message = No important warnings
 Generation =     1, Model     1,           Done,    fitness =  7615.266,    message = No important warnings
 Generation =     1, Model     3,           Done,    fitness =  7615.266,    message = No important warnings
 Generation =     1, Model     4,           Done,    fitness =  7406.655,    message = No important warnings
 Current generation best genome = [0, 1, 1, 0], best fitness = 7406.6551
 Best overall fitness = 7406.655099, iteration 1, model 4
 No change in fitness for 1 generations, best fitness = 7406.6551
 Starting generation 2
 Time elapsed: 1.4 min.
 Estimated time remaining: 89 min.
 Generation =     2, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     2, Model     2,     Cache(1-2),    fitness =          ,    message = From NLME_1_2: No important warnings
 Generation =     2, Model     3,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     2, Model     4,     Cache(1-1),    fitness =          ,    message = From NLME_1_1: No important warnings
 Starting downhill generation = 2  at Wed Nov  1 16:00:27 2023
 current best model(s) =
 generation 2, ind 1, fitness = 7406.655099211558
 generation 2, ind 3, fitness = 7406.655099211558
 code for niche (minimal binary) 1 = [0, 1, 1, 0], fitness = 7406.655099211558
 Starting downhill step 1, total of 4 in 1 niches to be run.
 Time elapsed: 1.4 min.
 Estimated time remaining: 86 min.
 Generation =  2D01, Model     1,     Cache(1-1),    fitness =          ,    message = From NLME_1_1: No important warnings
 Generation =  2D01, Model     3,     Cache(1-2),    fitness =          ,    message = From NLME_1_2: No important warnings
 Generation =  2D01, Model     2,           Done,    fitness =  8705.072,    message = No important warnings
 Generation =  2D01, Model     4,           Done,    fitness =  8843.169,    message = No important warnings
 Begin local exhaustive 2-bit search, generation = 2, step = 2
 Model for local exhaustive search = 2, phenotype = ([('PML', 0), ('_F', 1), ('_Rate', 1), ('_nlogitF', 0)]) model Num = 1, fitness = 7406.655099211558
 4 models in local exhaustive search, 1 bits
 10 models in local exhaustive search, 2 bits
 Time elapsed: 6.2 min.
 Estimated time remaining: 3 h. 43 min.
 Generation =  2S01, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  2S01, Model     3,     Cache(1-3),    fitness =          ,    message = From NLME_1_3: No important warnings
 Generation =  2S01, Model     5,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  2S01, Model     6,           Done,    fitness =  7549.176,    message = No important warnings
 Generation =  2S01, Model     8,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  2S01, Model     2,           Done,    fitness =  7562.779,    message = No important warnings
 Generation =  2S01, Model    10,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  2S01, Model     9,           Done,    fitness =  7564.889,    message = No important warnings
 Generation =  2S01, Model     4,           Done,    fitness =  7538.376,    message = No important warnings
 Generation =  2S01, Model     7,           Done,    fitness =  8705.072,    message = No important warnings
 Done with downhill step, 2. best fitness = 7406.655099211558
 Current generation best genome = [0, 1, 1, 0], best fitness = 7406.6551
 Best overall fitness = 7406.655099, iteration 1, model 4
 No change in fitness for 2 generations, best fitness = 7406.6551
 Starting generation 3
 Time elapsed: 9.4 min.
 Estimated time remaining: 2 h. 48 min.
 Generation =     3, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     3, Model     2,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     3, Model     3,     Cache(1-1),    fitness =          ,    message = From NLME_1_1: No important warnings
 Generation =     3, Model     4,       Clone(3),    fitness =          ,    message = From NLME_1_1: No important warnings
 Current generation best genome = [0, 1, 1, 0], best fitness = 7406.6551
 Best overall fitness = 7406.655099, iteration 1, model 4
 No change in fitness for 3 generations, best fitness = 7406.6551
 Starting generation 4
 Time elapsed: 9.4 min.
 Estimated time remaining: 2 h. 44 min.
 Generation =     4, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     4, Model     2,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     4, Model     3,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     4, Model     4,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Starting downhill generation = 4  at Wed Nov  1 16:08:30 2023
 current best model(s) =
 generation 4, ind 1, fitness = 7406.655099211558
 generation 4, ind 2, fitness = 7406.655099211558
 code for niche (minimal binary) 1 = [0, 1, 1, 0], fitness = 7406.655099211558
 Starting downhill step 1, total of 4 in 1 niches to be run.
 Time elapsed: 9.4 min.
 Estimated time remaining: 2 h. 37 min.
 Generation =  4D01, Model     1,     Cache(1-1),    fitness =          ,    message = From NLME_1_1: No important warnings
 Generation =  4D01, Model     2,  Cache(2D01-2),    fitness =          ,    message = From NLME_2D01_2: No important warnings
 Generation =  4D01, Model     3,     Cache(1-2),    fitness =          ,    message = From NLME_1_2: No important warnings
 Generation =  4D01, Model     4,  Cache(2D01-4),    fitness =          ,    message = From NLME_2D01_4: No important warnings
 Begin local exhaustive 2-bit search, generation = 4, step = 2
 Model for local exhaustive search = 4, phenotype = ([('PML', 0), ('_F', 1), ('_Rate', 1), ('_nlogitF', 0)]) model Num = 1, fitness = 7406.655099211558
 4 models in local exhaustive search, 1 bits
 10 models in local exhaustive search, 2 bits
 Time elapsed: 9.4 min.
 Estimated time remaining: 2 h. 06 min.
 Generation =  4S01, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  4S01, Model     2,  Cache(2S01-2),    fitness =          ,    message = From NLME_2S01_02: No important warnings
 Generation =  4S01, Model     3,     Cache(1-3),    fitness =          ,    message = From NLME_1_3: No important warnings
 Generation =  4S01, Model     4,  Cache(2S01-4),    fitness =          ,    message = From NLME_2S01_04: No important warnings
 Generation =  4S01, Model     5,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  4S01, Model     6,  Cache(2S01-6),    fitness =          ,    message = From NLME_2S01_06: No important warnings
 Generation =  4S01, Model     7,  Cache(2S01-7),    fitness =          ,    message = From NLME_2S01_07: No important warnings
 Generation =  4S01, Model     8,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  4S01, Model     9,  Cache(2S01-9),    fitness =          ,    message = From NLME_2S01_09: No important warnings
 Generation =  4S01, Model    10,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Done with downhill step, 4. best fitness = 7406.655099211558
 Current generation best genome = [0, 1, 1, 0], best fitness = 7406.6551
 Best overall fitness = 7406.655099, iteration 1, model 4
 No change in fitness for 4 generations, best fitness = 7406.6551
 Starting generation 5
 Time elapsed: 9.4 min.
 Estimated time remaining: 109 min.
 Generation =     5, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     5, Model     2,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     5, Model     3,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     5, Model     4,  Cache(2S01-4),    fitness =          ,    message = From NLME_2S01_04: No important warnings
 Current generation best genome = [0, 1, 1, 0], best fitness = 7406.6551
 Best overall fitness = 7406.655099, iteration 1, model 4
 No change in fitness for 5 generations, best fitness = 7406.6551
 Starting generation 6
 Time elapsed: 9.4 min.
 Estimated time remaining: 106 min.
 Generation =     6, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     6, Model     2,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =     6, Model     3,  Cache(2S01-9),    fitness =          ,    message = From NLME_2S01_09: No important warnings
 Generation =     6, Model     4,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Starting downhill generation = 6  at Wed Nov  1 16:08:30 2023
 current best model(s) =
 generation 6, ind 1, fitness = 7406.655099211558
 generation 6, ind 2, fitness = 7406.655099211558
 code for niche (minimal binary) 1 = [0, 1, 1, 0], fitness = 7406.655099211558
 Starting downhill step 1, total of 4 in 1 niches to be run.
 Time elapsed: 9.4 min.
 Estimated time remaining: 99 min.
 Generation =  6D01, Model     1,     Cache(1-1),    fitness =          ,    message = From NLME_1_1: No important warnings
 Generation =  6D01, Model     2,  Cache(2D01-2),    fitness =          ,    message = From NLME_2D01_2: No important warnings
 Generation =  6D01, Model     3,     Cache(1-2),    fitness =          ,    message = From NLME_1_2: No important warnings
 Generation =  6D01, Model     4,  Cache(2D01-4),    fitness =          ,    message = From NLME_2D01_4: No important warnings
 Begin local exhaustive 2-bit search, generation = 6, step = 2
 Model for local exhaustive search = 6, phenotype = ([('PML', 0), ('_F', 1), ('_Rate', 1), ('_nlogitF', 0)]) model Num = 1, fitness = 7406.655099211558
 4 models in local exhaustive search, 1 bits
 10 models in local exhaustive search, 2 bits
 Time elapsed: 9.4 min.
 Estimated time remaining: 68 min.
 Generation =  6S01, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  6S01, Model     2,  Cache(2S01-2),    fitness =          ,    message = From NLME_2S01_02: No important warnings
 Generation =  6S01, Model     3,     Cache(1-3),    fitness =          ,    message = From NLME_1_3: No important warnings
 Generation =  6S01, Model     4,  Cache(2S01-4),    fitness =          ,    message = From NLME_2S01_04: No important warnings
 Generation =  6S01, Model     5,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  6S01, Model     6,  Cache(2S01-6),    fitness =          ,    message = From NLME_2S01_06: No important warnings
 Generation =  6S01, Model     7,  Cache(2S01-7),    fitness =          ,    message = From NLME_2S01_07: No important warnings
 Generation =  6S01, Model     8,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation =  6S01, Model     9,  Cache(2S01-9),    fitness =          ,    message = From NLME_2S01_09: No important warnings
 Generation =  6S01, Model    10,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Done with downhill step, 6. best fitness = 7406.655099211558
 Current generation best genome = [0, 1, 1, 0], best fitness = 7406.6551
 Best overall fitness = 7406.655099, iteration 1, model 4
 No change in fitness for 6 generations, best fitness = 7406.6551
 -- End of GA component at Wed Nov  1 16:08:30 2023 --
0] Best individual GA is [0, 1, 1, 0] with fitness of 7406.655099
 code for niche (minimal binary) 1 = [0, 1, 1, 0], fitness = 7406.655099211558
 Starting downhill step 1, total of 4 in 1 niches to be run.
 Time elapsed: 9.4 min.
 Estimated time remaining: 48 min.
 Generation = FND01, Model     1,     Cache(1-1),    fitness =          ,    message = From NLME_1_1: No important warnings
 Generation = FND01, Model     2,  Cache(2D01-2),    fitness =          ,    message = From NLME_2D01_2: No important warnings
 Generation = FND01, Model     3,     Cache(1-2),    fitness =          ,    message = From NLME_1_2: No important warnings
 Generation = FND01, Model     4,  Cache(2D01-4),    fitness =          ,    message = From NLME_2D01_4: No important warnings
 Begin local exhaustive 2-bit search, generation = FN, step = 2
 Model for local exhaustive search = 6, phenotype = ([('PML', 0), ('_F', 1), ('_Rate', 1), ('_nlogitF', 0)]) model Num = 1, fitness = 7406.655099211558
 4 models in local exhaustive search, 1 bits
 10 models in local exhaustive search, 2 bits
 Time elapsed: 9.4 min.
 Estimated time remaining: 17 min.
 Generation = FNS01, Model     1,     Cache(1-4),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation = FNS01, Model     2,  Cache(2S01-2),    fitness =          ,    message = From NLME_2S01_02: No important warnings
 Generation = FNS01, Model     3,     Cache(1-3),    fitness =          ,    message = From NLME_1_3: No important warnings
 Generation = FNS01, Model     4,  Cache(2S01-4),    fitness =          ,    message = From NLME_2S01_04: No important warnings
 Generation = FNS01, Model     5,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation = FNS01, Model     6,  Cache(2S01-6),    fitness =          ,    message = From NLME_2S01_06: No important warnings
 Generation = FNS01, Model     7,  Cache(2S01-7),    fitness =          ,    message = From NLME_2S01_07: No important warnings
 Generation = FNS01, Model     8,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Generation = FNS01, Model     9,  Cache(2S01-9),    fitness =          ,    message = From NLME_2S01_09: No important warnings
 Generation = FNS01, Model    10,       Clone(1),    fitness =          ,    message = From NLME_1_4: No important warnings
 Done with final downhill step. best fitness = 7406.655099211558
 Final output from best model is in /tmp/RtmplOWzal/Results/output/FinalResultFile.txt
 Number of considered models: 80
 Number of models that were run during the search: 11
 Number of unique models to best model: 4
 Time to best model: 1.4 minutes
 Best overall fitness: 7406.655099, iteration 1, model 4
 Elapsed time: 9.4 minutes 

 Search end time: Wed Nov  1 16:08:30 2023

 Key models:
 Generation =     1, Model     4,           Done,    fitness =  7406.655,    message = No important warnings

The results will be available as a list with elements including results (a data frame with results of all runs), FinalResultFile (textual file with the properties of final model chosen) and FinalControlFile with the final metamodel as a result of the search.

Conclusion

This vignette demonstrates a complete workflow for using Certara.RDarwin to configure and execute a model search with the pyDarwin framework. It highlights the importance of setting up the model, configuring parameters, and creating the necessary files for a successful model selection process. By following these steps, users can effectively perform automated model selection using RDarwin and pyDarwin.