Skip to contents

RsNLME package logo

This guide explains how to run Certara’s Certara.RsNLME R package on a remote server, allowing you to leverage more powerful computing resources for your pharmacometric modeling tasks.

To get started, you’ll need to set up both your local machine (the client) and the remote server. Let’s walk through the prerequisites for each.

Setting Up Your Local Machine (Client)

Client System Requirements

RsNLME remote execution is supported on the following operating systems:

  • Windows 10
  • Windows 11
  • Windows Server 2016, 2019, 2022
  • Linux CentOS8/RHEL8
  • Linux CentOS9/RHEL9
  • Linux Ubuntu 22.04, 24.04

Client Installation Prerequisites

  • NLME Engine and RsNLME Package: You need to have the NLME Engine and the Certara.RsNLME package installed on your local machine. Refer to the RsNLME Installation Guide for detailed instructions. You’ll need the Certara.RsNLME package and its dependencies. Other Certara R packages can be installed but aren’t strictly required for remote execution.

Client Licensing

  • Local License: You need a valid NLME license on your local machine. See the Authentication and Licensing section of the installation guide.
  • Automatic Remote Licensing: Certara.RsNLME will automatically configure the necessary licensing information on the remote server if it’s not already set up.

Setting Up the Remote Server

Server System Requirements

The remote server should meet the following requirements:

  • Operating System: Linux CentOS/RHEL or Linux Ubuntu.
  • openssh-server software: Required for secure remote connections.
  • OpenMPI library: Installed and configured if you plan to use MPI for parallel processing (optional).
  • Grid System (Optional): A grid system like SGE, TORQUE, LSF, or SLURM can be installed and configured for enhanced parallel execution.

Server Installation Prerequisites

Refer to the RsNLME Installation Guide, specifically the Installation of NLME Engine and Installation of R Packages sections.

Important: The only required R package on the remote server is Certara.NLME8 (it should be installed manually). You don’t need to install the full suite of RsNLME packages on the server.

To install Certara.NLME8 on the remote server, run the following command:

# Execute this code on the remote server
install.packages("Certara.NLME8", 
  repos = c("https://certara.jfrog.io/artifactory/certara-cran-release-public/", 
            "https://cloud.r-project.org"))

Server Licensing

RsNLME will automatically take care of configuring the necessary licenses on the remote server.

However, if you prefer to manage licenses manually, you can do so by following the instructions in the Authentication and Licensing section of the RsNLME Installation Guide.


Configuring Remote Hosts for Different Run Types

Simple Remote Host (No Parallelization)

A simple remote host is suitable for running models without parallelization (e.g., fitting a model with a small number of fixed effects or omega parameters).

The following R code demonstrates how to configure a simple remote host. You’ll need to modify the settings (e.g., paths, host name, user name) to match your environment before running it on your local machine.

# Example model
model <- emaxmodel(
  checkBaseline = TRUE,
  checkFractional = TRUE,
  checkInhibitory = TRUE,
  data = pkpdData,
  ID = "ID",
  C = "CObs",
  EObs = "EObs"
)

remoteSimpleHost <- hostParams(
  sharedDirectory = "/home/user/temp",                 # Directory on the remote server to store model files (must exist)
  installationDirectory = "/home/user/InstallDirNLME", # Directory where NLME Engine is installed on the remote server
  hostName = "remoteSimpleHost",                       # Internal name for the host (not used in computations)
  machineName = "192.168.1.1",                         # IP address or hostname of the remote server 
  hostType = "UBUNTU",                                 # Remote server's operating system (UBUNTU or RHEL)
  numCores = 1,                                        # Number of cores to use (1 for no parallelization)
  isLocal = FALSE,                                     # Set to FALSE for remote execution
  rLocation = "/usr/local/bin",                        # Path to the R executable on the remote server
  userName = "user",                                   # Your username on the remote server
  privateKeyFile = "c:/private/pass.key",              # Path to your private SSH key file (optional, see below)
  parallelMethod = "None"                              # No parallelization method
)

# Note: Avoid using shortcuts like ~ (tilde) in remote paths

# Use the host for remote model fitting:
fitmodelResults <- fitmodel(model = model, hostPlatform = remoteSimpleHost)

Using SSH Keys:

The example above uses a private SSH key for authentication instead of a password. This is generally more secure. Consult your server administrator or refer to resources like PuTTYgen and the ssh package documentation for information on generating SSH keys.

How RsNLME Handles Remote Execution:

RsNLME will automatically:

  • Transfer the necessary files to the specified remote directory.
  • Report the status of iterations during the model run.
  • Download the results from the remote directory when the run is complete.
  • Load the result data frames into your R environment on your local machine.

Running in the Background:

On a remote Linux host you can launch this fit asynchronously with runInBackground = TRUE and retrieve it later with collectJob(). See Running Jobs in the Background below for the full workflow.

Remote Host with MPI Parallelization

To take advantage of OpenMPI for parallel processing, you can configure your remote host as follows. This example uses a shell script to set the PhoenixMPIDir64 environment variable on the remote server:

  # /home/user/MPIEnable.sh content
  export PhoenixMPIDir64=/lib64/openmpi/  

By using a script, you don’t need to set PhoenixMPIDir64 in your .bashrc file.

MPI parallelization is ideal for fitting complex models with many thetas, omegas, or ODEs. It will parallelize the model fitting process by subject (except for the QRPEM engine, which parallelizes by samples).

# Example model
model <- pkemaxmodel(
  parameterization = "Clearance",
  data = pkpdData,
  Time = "Time",
  ID = "ID",
  A1 = "Dose",
  C1Obs = "CObs",
  EObs = "EObs"
)

remoteMPIHost <- hostParams(
  sharedDirectory = "/home/user/temp",                 # Directory on the remote server to store model files
  installationDirectory = "/home/user/InstallDirNLME", # Directory where NLME Engine is installed on the remote server
  hostName = "remoteMPIHost",                          # Internal name for the host
  machineName = "192.168.1.1",                         # IP address or hostname of the remote server
  hostType = "UBUNTU",                                 # Remote server's operating system
  numCores = 16,                                       # Number of cores to use
  isLocal = FALSE,                                     # Set to FALSE for remote execution
  rLocation = "/usr/local/bin",                        # Path to the R executable on the remote server
  scriptPath = "/home/user/MPIEnable.sh",              # Path to the script that sets up the MPI environment
  userName = "user",                                   # Your username on the remote server
  privateKeyFile = "c:/private/pass.key",             # Path to your private SSH key file
  parallelMethod = "MPI"                               # Use MPI for parallelization
)

# Use the host for remote model fitting:
fitmodelResults <- fitmodel(model = model, 
                            hostPlatform = remoteMPIHost)

The results will be available as a list in the specified variable (fitmodelResults in this case) and downloaded to the model directory on your local machine.

Troubleshooting:

If you encounter errors and the console output isn’t helpful, check the NlmeRemote.LOG file on your local machine for more detailed information.

Multicore Remote Host (Parallel Jobs)

For tasks that can be run as multiple independent jobs in parallel, you can use a multicore remote host. This approach lets the operating system distribute the jobs across the available cores. Suitable run types include:

  • sortfit
  • shotgunSearch
  • stepwiseSearch
  • bootstrap
input_data <- pkData

# Add gender code column
input_data$GenderCode = 0
input_data$GenderCode[input_data$Gender == "male"] = 1

2CMTModel <-
  pkmodel(
    numCompartments = 2,
    data = input_data,
    ID = "Subject",
    Time = "Act_Time",
    A1 = "Amount",
    CObs = "Conc"
  )

2CMTModel <-
  addCovariate(
    2CMTModel,
    covariate = "Gender",
    type = "Categorical",
    effect = c("V2", "Cl2"),
    levels = c(0, 1),
    labels = c("Female", "Male")
  )

2CMTModel <-
  addCovariate(
    2CMTModel,
    covariate = "BodyWeight",
    direction = "Backward",
    center = "Mean",
    effect = c("V", "Cl")
  )

CovariateEffectNames <-
  listCovariateEffectNames(2CMTModel)
combinations <-
  combn(c("", CovariateEffectNames),
        length(CovariateEffectNames),
        simplify = FALSE)

scenarioNames <-
  lapply(combinations,
         function(x) {paste(x, collapse = " ")})

scenarios <-
  lapply(scenarioNames,
         function(x, CovariateEffectNames) {
           CovariateCombinations <- unlist(strsplit(x, " ", fixed = TRUE))
           scenarioIndex <-
             paste(which(CovariateEffectNames %in% CovariateCombinations,
                         arr.ind = TRUE),
                   collapse = ", ")
           NlmeScenario(trimws(x), scenarioIndex)
         },
         CovariateEffectNames)

remoteMULTICOREHost <- hostParams(
  sharedDirectory = "/home/user/temp",                 # Directory on the remote server to store model files
  installationDirectory = "/home/user/InstallDirNLME", # Directory where NLME Engine is installed on the remote server
  hostName = "remoteMULTICOREHost",                    # Internal name for the host
  machineName = "192.168.1.1",                         # IP address or hostname of the remote server
  hostType = "UBUNTU",                                 # Remote server's operating system
  numCores = 16,                                       # Number of parallel jobs to run
  isLocal = FALSE,                                     # Set to FALSE for remote execution
  rLocation = "/usr/local/bin",                        # Path to the R executable on the remote server
  userName = "user",                                   # Your username on the remote server
  privateKeyFile = "c:/private/pass.key",             # Path to your private SSH key file
  parallelMethod = "Multicore"                         # Use multicore parallelization
)

# Use the host for remote model fitting:
res <-
  sortfit(2CMTModel,
          hostPlatform = remotemulticoreHost,
          sortColumns = SortColumns("Gender"),
          scenarios = scenarios)

Grid Remote Host (Job Scheduling)

If your remote server has a supported grid system (Torque, SGE, LSF, or SLURM), you can submit jobs to the grid for execution. Specify the appropriate grid system using the parallelMethod argument in hostParams().

remoteSLURMHost <- hostParams(
  sharedDirectory = "/home/user/temp",                 # Directory on the remote server to store model files
  installationDirectory = "/home/user/InstallDirNLME", # Directory where NLME Engine is installed on the remote server
  hostName = "remoteSLURMHost",                        # Internal name for the host
  machineName = "192.168.1.2",                         # IP address or hostname of the remote server
  hostType = "UBUNTU",                                 # Remote server's operating system
  numCores = 16,                                       # Number of nodes to use 
  isLocal = FALSE,                                     # Set to FALSE for remote execution
  rLocation = "/usr/local/bin",                        # Path to the R executable on the remote server
  scriptPath = "/home/user/SLURMEnable.sh",            # Path to a script to initialize the grid (if needed)
  userName = "user",                                   # Your username on the remote server
  privateKeyFile = "c:/private/pass.key",             # Path to your private SSH key file 
  parallelMethod = "SLURM"                             # Use SLURM for parallelization 
)

# Use the host for remote model fitting:
shotgunSearchResults <- shotgunSearch(model = 2CMTModel, 
                                      hostPlatform = remoteSLURMHost)

remoteSGEHost <- hostParams(
  sharedDirectory = "/home/user/temp",                 # Directory on the remote server to store model files
  installationDirectory = "/home/user/InstallDirNLME", # Directory where NLME Engine is installed on the remote server
  hostName = "remoteSGEHost",                          # Internal name for the host
  machineName = "192.168.1.3",                         # IP address or hostname of the remote server
  hostType = "UBUNTU",                                 # Remote server's operating system
  numCores = 16,                                       # Number of nodes to use
  isLocal = FALSE,                                     # Set to FALSE for remote execution
  rLocation = "/usr/local/bin",                        # Path to the R executable on the remote server
  scriptPath = "/home/user/SGEEnable.sh",              # Path to a script to initialize the grid (if needed)
  userName = "user",                                   # Your username on the remote server
  privateKeyFile = "c:/private/pass.key",             # Path to your private SSH key file
  parallelMethod = "SGE"                               # Use SGE for parallelization 
)

stepwiseSearch(model = model,
               hostPlatform = remoteSGEHost)

Important: Don’t overload the grid by requesting more cores (numCores) than are available. This can lead to errors.

Queue Routing and Job Observability

When submitting to a grid, you can control which queue (or SLURM partition) the work lands on, request wall-clock and memory limits, and give jobs informative names so they are easy to spot in qstat/squeue. These are configured once on the host and apply to every job run on it. They are only used by grid parallel methods ("SGE", "TORQUE", "LSF", "SLURM", and their _MPI variants); on local methods they are ignored with a warning.

gridHost <- hostParams(
  sharedDirectory = "/home/user/temp",
  installationDirectory = "/home/user/InstallDirNLME",
  hostName = "remoteSGEHost",
  machineName = "192.168.1.3",
  hostType = "UBUNTU",
  numCores = 32,
  isLocal = FALSE,
  rLocation = "/usr/local/bin",
  userName = "user",
  privateKeyFile = "c:/private/pass.key",
  parallelMethod = "SGE_MPI",
  gridQueue = "bg.q",            # Route long batch work to a non-interactive queue
  gridMemory = "16G",           # Request 16 GB (SGE h_vmem / TORQUE vmem / SLURM --mem)
  gridJobNamePrefix = "BOOT_prod", # qstat/squeue job name prefix
  gridResourceExtra = "h_data=4G" # Site-specific resource appended verbatim
)

bootstrap(model = model, hostPlatform = gridHost, numReplicates = 1000)

Notes on each setting:

  • gridQueue is the scheduler’s queue (SGE/TORQUE/LSF) or partition (SLURM) name for CPU jobs. It does not imply GPU or graphical execution. Leave it empty (the default) to use the cluster’s default queue. A common pattern is to send long-running, batch-style work (bootstrap, covariate search) to a non-interactive queue such as bg.q, and shorter interactive fits to the default all.q.
  • gridWalltime is a wall-clock limit in seconds. It defaults to unset (NA) so long-running jobs are not capped; supply a positive integer only when your site requires a limit.
  • gridMemory is passed through to the scheduler’s memory request.
  • gridJobNamePrefix makes jobs identifiable in qstat/squeue. When empty, a name is derived from the workflow and job type (e.g. BOOT_<workflow>). Only letters, digits, and underscores are kept.
  • gridResourceExtra is emitted verbatim using each scheduler’s native syntax: appended to a resource list for the PBS-style schedulers (SGE #$ -l <value>, TORQUE #PBS -l <value>, e.g. "h_data=4G"), or rendered as a standalone directive for SLURM (#SBATCH <value>, e.g. "--gres=gpu:1") and LSF (#BSUB <value>, e.g. "-R rusage[mem=4096]"). Newlines and control characters are rejected.

Grid Remote Host with MPI Parallelization (Combined Parallelization)

For very large datasets with many replicates that require significant processing time, you can combine grid-based job scheduling with MPI parallelization within each job (e.g., parallelizing by both subject and replicate). This is useful for tasks like bootstrap, covariate search, or sortfit.

# Parallelization by subjects and replicates (suitable for bootstrap, 
# covariate search, or sortfit)

remoteSLURMMPIHost <- hostParams(
  sharedDirectory = "/home/user/temp",                 # Directory on the remote server to store model files 
  installationDirectory = "/home/user/InstallDirNLME", # Directory where NLME Engine is installed on the remote server
  hostName = "remoteSLURMMPIHost",                     # Internal name for the host
  machineName = "192.168.1.3",                         # IP address or hostname of the remote server 
  hostType = "UBUNTU",                                 # Remote server's operating system
  numCores = 16,                                       # Total number of cores available
  isLocal = FALSE,                                     # Set to FALSE for remote execution
  rLocation = "/usr/local/bin",                        # Path to the R executable on the remote server
  scriptPath = "/home/user/SLURM_MPIEnable.sh",        # Optional path to a script for grid initialization and
                                                       # setting PhoenixMPIDir64 (if not in .bash_profile)
  userName = "user",                                   # Your username on the remote server
  privateKeyFile = "c:/private/pass.key",             # Path to your private SSH key file
  parallelMethod = "SGE_MPI"                           # Use SLURM and MPI for parallelization
)

# Use the host for remote model fitting:
bootstrapResults <- bootstrap(model = model, 
                              hostPlatform = remoteSLURMMPIHost,
                              numReplicates = 1000)

Automatic Core Distribution:

RsNLME will attempt to find the optimal distribution of nodes and MPI cores per node, considering the number of subjects and jobs. The number of cores used for each replicate will be the smaller of these two values:

  1. The total number of available cores divided by the number of replicates.
  2. The number of unique subjects in the replicate divided by 3.

Examples:

  • Example 1: 300 cores available, 2000 replicates requested, 200 unique subjects. Each replicate would use 1 core (300/2000 < 1, 200/3 = 66, 1 < 66). Total cores used: 300.
  • Example 2: 1000 cores available, 200 replicates requested, 300 unique subjects. Each replicate would use 5 cores (1000/200 = 5, 300/3 = 100, 5 < 100). Total cores used: 1000.

Running Jobs in the Background

Long-running remote work – a large bootstrap, a covariate search, or a fit on a big dataset – does not have to block your R session. Every job wrapper accepts runInBackground = TRUE, which launches the run detached on the host and returns immediately with a lightweight job object instead of the parsed results. You then poll or collect that job whenever you are ready.

Platform rule: background execution is supported only on Linux hosts, local or remote (hostType "linux", "RHEL", or "UBUNTU"). On Windows, leave runInBackground = FALSE; passing TRUE stops with an error. On a remote Linux host the results stay on the server until you collect them – they are not automatically downloaded to your local session the way a foreground run’s results are.

Launching and collecting a job

The following wrappers support runInBackground = TRUE:

Each returns a typed job object (e.g. FitNlmeJob, BootNlmeJob, ShotgunNlmeJob). collectJob() is the single canonical entry point for retrieving the result: it polls the job via NlmeJobStatus() until the engine reports "Finished", downloads the remote artifacts (when applicable), parses them, and returns the same result object the foreground call would have produced. There is no separate per-wrapper collector to remember.

# Launch a fit in the background on a remote Linux host.
fitJob <- fitmodel(
  model        = model,
  hostPlatform = remoteMPIHost,
  runInBackground = TRUE
)

# Check status without blocking (returns an NlmeJobStatus value).
NlmeJobStatus(fitJob)

# Block until the run finishes, then materialise the typed result.
# `timeout` caps the wait (seconds; Inf waits indefinitely) and
# `pollInterval` sets the seconds between status checks.
fitResults <- collectJob(fitJob, timeout = Inf, pollInterval = 30)

print(fitResults$Overall)

collectJob() dispatches on the job type, so the returned value matches the wrapper you launched:

Backgrounded wrapper collectJob() returns
fitmodel(), sortfit() the fitmodel() / sortfit() result list
bootstrap() an rsnlme_boot object
stepwiseSearch(), shotgunSearch() an scmSearchResult
profilePertubate() a profileResult data frame
vpcmodel(), simmodel() the named list of simulation data.tables

Example: a backgrounded bootstrap

# Launch a 1000-replicate bootstrap detached on the grid+MPI host.
bootJob <- bootstrap(
  model        = model,
  hostPlatform = remoteSLURMMPIHost,
  numReplicates = 1000,
  runInBackground = TRUE
)

# ... continue working in R, or even come back in the same session later ...

# Collect when ready (polling every 60 seconds).
bootResult <- collectJob(bootJob, pollInterval = 60)

bootResult

Saved results: for the wrappers that persist output (fitmodel(), sortfit(), bootstrap(), profilePertubate()), collectJob() also writes a self-describing snapshot of the result to the local working directory as <jobType>_<sanitizedModelName>_<YYYYMMDD_HHMMSS>.rds (e.g. bootstrap_MyModel_20260831_084500.rds) unless the run was launched with saveResult = FALSE. That .rds can be re-read in a fresh R session and passed straight to downstream tools such as xposeNlmeModel().