using Pumas
using DataFramesMeta
using CategoricalArrays
using AlgebraOfGraphics
using CairoMakie
using Random
Model Introduction
Understanding the HBV QSP Model and Treatment Mechanisms
This is Tutorial 1 of 6 in the HBV series.
| # | Tutorial | Status |
|---|---|---|
| 1 | Model Introduction (current) | ← You are here |
| 2 | Global Sensitivity Analysis | |
| 3 | Structural Identifiability | |
| 4 | Copula Vpop Generation | |
| 5 | MILP Calibration | |
| 6 | VCT Simulation |
1 Learning Outcomes
Hepatitis B virus (HBV) infection is a major global health challenge, with over 290 million people chronically infected. Mathematical models help us understand treatment mechanisms and predict clinical outcomes.
In this tutorial, you will learn:
- The biology of HBV infection and why it’s difficult to cure
- The structure of the 11-ODE QSP model capturing HBV dynamics
- The treatment mechanisms of NA (nucleoside analogs) and IFN (interferon)
- How to define and simulate the HBV model in Pumas
- Key clinical endpoints: Functional Cure and HBsAg Loss
This tutorial assumes basic familiarity with:
- Pumas model syntax (
@param,@dynamics,@derived) - Ordinary differential equations (ODEs)
- Pharmacokinetic/pharmacodynamic modeling concepts
If you’ve completed the Tumor Burden Tutorial Series, you have all the background needed.
2 Background
2.1 The HBV Infection Challenge
Hepatitis B Virus (HBV) infects liver cells (hepatocytes) and establishes chronic infection in many patients. Unlike hepatitis C, which can now be cured with antivirals, HBV is much harder to eliminate because:
- cccDNA persistence: The virus inserts its genetic material (cccDNA) into the liver, creating a reservoir that survives treatment
- Immune exhaustion: Chronic infection leads to T-cell exhaustion, reducing the body’s ability to clear infected cells
- HBsAg shedding: Infected cells produce large amounts of surface antigen (HBsAg) that may “decoy” the immune system
2.2 Treatment Goals
Current treatments aim for different levels of control:
| Endpoint | Definition | Clinical Meaning |
|---|---|---|
| Viral Suppression | HBV DNA < 10^1.4 copies/mL | Virus under control (but not eliminated) |
| HBsAg Loss | HBsAg < 0.05 IU/mL | Surface antigen cleared |
| Functional Cure | HBsAg Loss + Viral Suppression for ≥24 weeks off-treatment | Durable control without ongoing treatment |
Current treatments (NA therapy) can suppress virus indefinitely, but patients must take medication for life. Functional cure means patients can stop treatment without viral rebound— the “holy grail” of HBV therapy.
2.3 Treatment Modalities
Nucleoside/Nucleotide Analogs (NAs):
- Examples: Entecavir, Tenofovir
- Mechanism: Block viral replication by inhibiting reverse transcriptase
- Effect: Rapidly suppress HBV DNA, slowly reduce HBsAg
- Limitation: Rarely achieve functional cure alone
Interferon (IFN):
- Examples: Pegylated interferon-alpha (PEG-IFN)
- Mechanism: Enhances immune response, promotes infected cell killing
- Effect: Boosts T-cell activity, accelerates HBsAg decline
- Limitation: Side effects limit duration, variable response
3 Libraries
4 Part 1: Model Structure Overview
4.1 The 11-ODE System
The HBV QSP model (Cortés‐Ríos et al., 2025) tracks 11 state variables organized into three compartments:
flowchart LR
subgraph Hepatocytes ["Hepatocyte Populations"]
T["T<br/>Target cells"]
I["I<br/>Infected cells"]
R["R<br/>Resistant cells"]
end
subgraph Viral ["Viral Dynamics"]
V["V<br/>Virus (HBV DNA)"]
S["S<br/>HBsAg"]
end
subgraph Immune ["Immune Response"]
E["E<br/>Effector T-cells"]
P["P<br/>Dendritic cells"]
Q["Q<br/>Delayed signal"]
X["X<br/>Cytotoxic effect"]
D["D<br/>Dead cell marker"]
A["A<br/>ALT proxy"]
end
%% Mass flow (solid): production, conversion, transfer
T -->|"infection"| I
I -->|"converts"| R
R -->|"reverts<br/>ρ_o_ρ_r"| T
I -->|"virus production<br/>p_V×ϵ_NA×ϵ_IFN"| V
I -->|"HBsAg production<br/>p_S"| S
D -->|"drives"| A
%% Modulation (dashed): catalysis, activation, inhibition
V -.->|"drives<br/>infection"| I
E -.->|"kills"| I
E -.->|"kills (m/n)"| T
E -.->|"kills (m/n)"| R
X -.->|"promotes<br/>conversion"| R
E -.->|"produces<br/>debris"| D
I -.->|"activates"| P
P -.->|"stimulates"| E
P -.->|"delays into"| Q
Q -.->|"exhausts"| E
I -.->|"activates"| X
S -.->|"inhibits<br/>(via HBsAg)"| X
V -.->|"inhibits<br/>(via HBsAg)"| X
style T fill:#ccffcc,stroke:#00cc00
style I fill:#ffcccc,stroke:#cc0000
style R fill:#ffffcc,stroke:#cccc00
style V fill:#cce5ff,stroke:#0066cc
style S fill:#cce5ff,stroke:#0066cc
style E fill:#ffccff,stroke:#cc00cc
4.2 State Variables Explained
| State | Symbol | Description | Units |
|---|---|---|---|
| Uninfected hepatocytes | T | Uninfected liver cells | cells |
| Infected hepatocytes | I | Cells producing virus | cells |
| Refractory hepatocytes | R | Cells resistant to cytotoxic effect | cells |
| Viral load | V | HBV DNA | copies/mL |
| Subviral particles containing surface antigen | S | HBsAg molecules | molecules |
| Effector T-cells | E | Immune cells that kill infected cells | cells |
| Dendritic cells | P | Antigen-presenting cells | cells |
| Delayed signal | Q | Immune activation delay | - |
| Cytotoxic effect | X | Cumulative killing signal | - |
| Dead cell marker | D | Products from cell death | - |
| ALT proxy | A | Liver inflammation marker | - |
4.3 Parameter Structure
The model has 23 fixed parameters (from literature/prior studies) and 9 estimated parameters (with inter-individual variability):
Estimated Parameters (with IIV):
| Parameter | Description | Typical Value (log10) |
|---|---|---|
| p_S | HBsAg production rate | 7.643 |
| β (beta) | HBV infectivity | -4.358 |
| m | Immune killing rate | 1.618 |
| k_P | Antigen presenting cell synthesis | -3.968 |
| k_A | ALT production rate | -4.869 |
| ϵ_NA | NA efficacy on HBV production | -1.485 |
| ϵ_IFN | IFN efficacy on HBV production | -1.466 |
| r_E_IFN | IFN immune boost | 0.313 |
| conv_E | Effector T-cell scaling | 2.745 |
Parameters are on the log10 scale with additive random effects. This achieves log-normal behavior when used in dynamics as 10^param:
Individual value = 10^(typical_value + η)
This is equivalent to multiplicative IIV on the natural scale.
5 Part 2: The Pumas Model Definition
Let’s build the HBV model.
5.1 Model
hbv_model = @model begin
@param begin
# Estimated parameters (log10 scale)
"Production/synthesis rate of HBsAg subviral particles from infected hepatocytes [molec./cell/d]"
tv_log10_p_S ∈ RealDomain(; lower = 2, upper = 12)
"HBV infectivity [mL/virus/d]"
tv_log10_β ∈ RealDomain(; lower = -10, upper = 0)
"Killing rate of infected hepatocytes by cellular immunity [mL/cells/d]"
tv_log10_m ∈ RealDomain(; lower = -3, upper = 5)
"Synthesis rate of antigen presenting cells [cells/d]"
tv_log10_k_P ∈ RealDomain()
"Synthesis rate of ALT from debris hepatocytes [10^3 U/cells/d]"
tv_log10_k_A ∈ RealDomain()
"PD efficacy of NA on HBV production []"
tv_log10_ϵ_NA ∈ RealDomain(; lower = -5, upper = 0)
"PD efficacy of IFNα on HBV production []"
tv_log10_ϵ_IFN ∈ RealDomain(; lower = -5, upper = 0)
"PD efficacy of IFNα on activation of cellular immunity []"
tv_log10_r_E_IFN ∈ RealDomain(; lower = 0, upper = 4)
"Scaling factor for observed effector CD4/8 T cell numbers []"
tv_log10_conv_E ∈ RealDomain()
# IIV of estimated parameters in log10 scale
Ω ∈ PDiagDomain(9)
# Fixed parameters
"Baseline of HBV [copies/mL]"
ini_V ∈ RealDomain(; lower = 0.0)
"Baseline of ALT [U/L]"
ini_A ∈ RealDomain(; lower = 0.0)
"Max. proliferation rate of uninfected hepatocytes [1/d]"
r_T ∈ RealDomain(; lower = 0.0)
"Max. proliferation rate of cellular immunity [1/d]"
r_E ∈ RealDomain(; lower = 0.0)
"Max. proliferation rate of innate immunity [1/d]"
r_X ∈ RealDomain(; lower = 0.0)
"Fraction of cellular immunity that killing uninfected and refractory cells []"
n ∈ RealDomain(; lower = 0.0)
"Max. rate of conversion of infected cells to refractory cells due to the action of innate immunity [1/d]"
ρ ∈ RealDomain(; lower = 0.0)
"Max. rate of conversion of refractory cells back to target cells in the absence of innate immune responses []"
ρ_o_ρ_r ∈ RealDomain(; lower = 0.0)
"Production/synthesis rate of HBV from infected hepatocytes [virus/cell/d]"
p_V ∈ RealDomain(; lower = 0.0)
"Death rate of infected hepatocytes [1/d]"
d_TI ∈ RealDomain(; lower = 0.0)
"Degradation/clearance of HBV HBsAg subviral particles [1/d]"
d_V ∈ RealDomain(; lower = 0.0)
"Rate of systemic induction/recovery from exhaustion [1/d]"
d_Q ∈ RealDomain(; lower = 0.0)
"Degradation/clearance of innate immunity [1/d]"
d_X ∈ RealDomain(; lower = 0.0)
"Degradation/clearance of cellular immunity [1/d]"
d_E ∈ RealDomain(; lower = 0.0)
"Degradation/clearance of antigen presenting cells [1/d]"
d_P ∈ RealDomain(; lower = 0.0)
"Degradation/clearance of debris [1/d]"
d_D ∈ RealDomain(; lower = 0.0)
"Degradation/clearance of ALT [1/d]"
d_A ∈ RealDomain(; lower = 0.0)
"Max. rate of exhaustion [1/d]"
d_E_Q ∈ RealDomain(; lower = 0.0)
"Hepatocyte carrying capacity in the liver [cells/mL]"
T_max ∈ RealDomain(; lower = 0.0)
"Half-maximal (saturation) parameter for the effect of systemic exhaustion []"
ϕ_Q ∈ RealDomain(; lower = 0.0)
"Half-maximal (saturation) parameter for the activation of cellular immunity [cells/mL]"
ϕ_E ∈ RealDomain(; lower = 0.0)
"Max. inhibitory effect of HBsAg subviral particles on innate immunity []"
S_max ∈ RealDomain(; lower = 0.0)
"Half-maximal (saturation) parameter for inhibitory effect of HBsAg subviral particles on the generation of innate immunity [μg/mL]"
ϕ_S ∈ RealDomain(; lower = 0.0)
"Hill coefficient for describing HBsAg-induced inhibition of innate immunity []"
nS ∈ RealDomain()
"Threshold of infected hepatocytes to produce dynamical switch from progressive disease to long-lasting control of HBV infection [cells/mL]"
I_threshold ∈ RealDomain(; lower = 0.0)
# Residual errors
σ_add_log10_HBsAg ∈ RealDomain(lower = 0.0)
σ_prop_log10_HBsAg ∈ RealDomain(lower = 0.0)
σ_add_log10_HBV ∈ RealDomain(lower = 0.0)
σ_prop_log10_HBV ∈ RealDomain(lower = 0.0)
σ_prop_log10_ALT ∈ RealDomain(lower = 0.0)
σ_prop_log10_CD8 ∈ RealDomain(lower = 0.0)
end
@random begin
η ~ MvNormal(Ω)
end
# Time-varying covariates for treatment phases
@covariates NA IFN
@pre begin
# Individual parameters
# Logit-normal distribution of log10(p_S), scaled to range [2, 12]
p_S = exp10(10 * logistic(logit((tv_log10_p_S - 2) / 10) + η[1]) + 2)
# Logit-normal distribution of log10(β), scaled to range [-10, 0]
β = exp10(10 * (logistic(logit(tv_log10_β / 10 + 1) + η[2]) - 1))
# Logit-normal distribution of log10(m), scaled to range [-3, 5]
m = exp10(8 * logistic(logit((tv_log10_m + 3) / 8) + η[3]) - 3)
# Normal distribution of log10(k_P)
k_P = exp10(tv_log10_k_P + η[4])
# Normal distribution of log10(k_A)
k_A = exp10(tv_log10_k_A + η[5])
# Logit-normal distribution of log10(ϵ_NA), scaled to range [-5, 0]
ϵ_NA = exp10(NA * 5 * (logistic(logit(tv_log10_ϵ_NA / 5 + 1) + η[6]) - 1))
# Logit-normal distribution of log10(ϵ_IFN), scaled to range [-5, 0]
ϵ_IFN = exp10(IFN * 5 * (logistic(logit(tv_log10_ϵ_IFN / 5 + 1) + η[7]) - 1))
# Logit-normal distribution of log10(r_E_IFN), scaled to range [0, 4]
r_E_IFN = exp10(IFN * 4 * logistic(logit(tv_log10_r_E_IFN / 4) + η[8]))
# Normal distribution
conv_E = exp10(tv_log10_conv_E + η[9])
end
# Initial conditions
@init begin
V = ini_V
I = d_V * V / p_V
T = T_max - I
S = p_S * I / d_V
A = ini_A
end
@dynamics begin
# Target uninfected hepatocytes
T' = -β * V * T + r_T * T * (1 - (T + Ieff + R) / T_max) -
d_TI * T + ρ_o_ρ_r * R - (m / n) * r_E_IFN * E * T
# Infected hepatocytes
I' = β * V * T + r_T * I * (1 - (T + I + R) / T_max) -
ρ * X * I - d_TI * I - m * r_E_IFN * E * I
# Resistant hepatocytes
R' = ρ * X * Ieff - ρ_o_ρ_r * R + r_T * R * (1 - (T + Ieff + R) / T_max) -
d_TI * R - (m / n) * r_E_IFN * E * R
# Dead cell marker
D' = m * r_E_IFN * E * Ieff + (m / n) * r_E_IFN * E * (T + R) - d_D * D
# Immune response (ALT proxy)
A' = d_A * (ini_A - A) + k_A * D
# HBsAg dynamics
S' = p_S * Ieff - d_V * S
# Viral load (NA and IFN reduce production)
V' = p_V * Ieff * ϵ_NA * ϵ_IFN - d_V * V
# Dendritic cell activation
P' = k_P * (Ieff / (ϕ_E + Ieff)) - d_P * P
# Effector T cells
E' = (d_E + r_E * E) * Peff - d_E_Q * E * (Q^4 / (ϕ_Q^4 + Q^4)) - d_E * E
# Delayed effector signal
Q' = d_Q * (Peff - Q)
# Cytotoxic effect
X' = r_X * (1 - X) * (Ieff / (ϕ_E + Ieff)) *
(1 - S_max * (HBsAg_ug_mL^nS / (ϕ_S^nS + HBsAg_ug_mL^nS))) - d_X * X
end
@vars begin
# Switch between progressive infection (I ≥ I_threshold) and clearance of infection (I < I_threshold)
# In the latter case, the system is forced to reach a steady state in which
# I = 0, viral load V = 0, HBsAg S = 0, and antigen presenting cells P = 0.
Ieff = ifelse(I < I_threshold, zero(I), I)
Peff = ifelse(I < I_threshold, zero(P), P)
# HBsAg [μg/mL]: Each particle of V and S is assumed to harbor 96 molecules of S envelope protein
f_molec2ug := 24000 * 1.0e6 / 6.022e23
HBsAg_ug_mL := 96 * (V + S) * f_molec2ug
end
# Error models: Values are log10-transformed
@derived begin
"HBsAg [IU/mL]"
log10_HBsAg ~ @. CombinedNormal(log10(max(0, HBsAg_ug_mL) / 0.98e-3), σ_add_log10_HBsAg, σ_prop_log10_HBsAg)
"Viral load [copies/mL]"
log10_HBV ~ @. CombinedNormal(log10(max(0, V)), σ_add_log10_HBV, σ_prop_log10_HBV)
"ALT [U/L]"
log10_ALT ~ @. ProportionalNormal(log10(max(0, A)), σ_prop_log10_ALT)
"CD8+ T cell counts [cells/mL]"
log10_CD8 ~ @. ProportionalNormal(log10(conv_E * max(0, E)), σ_prop_log10_CD8)
end
endPumasModel
Parameters: tv_log10_p_S, tv_log10_β, tv_log10_m, tv_log10_k_P, tv_log10_k_A, tv_log10_ϵ_NA, tv_log10_ϵ_IFN, tv_log10_r_E_IFN, tv_log10_conv_E, Ω, ini_V, ini_A, r_T, r_E, r_X, n, ρ, ρ_o_ρ_r, p_V, d_TI, d_V, d_Q, d_X, d_E, d_P, d_D, d_A, d_E_Q, T_max, ϕ_Q, ϕ_E, S_max, ϕ_S, nS, I_threshold, σ_add_log10_HBsAg, σ_prop_log10_HBsAg, σ_add_log10_HBV, σ_prop_log10_HBV, σ_prop_log10_ALT, σ_prop_log10_CD8
Random effects: η
Covariates: NA, IFN
Dynamical system variables: T, I, R, D, A, S, V, P, E, Q, X
Dynamical system type: Nonlinear ODE
Derived: log10_HBsAg, log10_HBV, log10_ALT, log10_CD8, Ieff, Peff
Observed: log10_HBsAg, log10_HBV, log10_ALT, log10_CD8, Ieff, Peff
5.2 Parameters
The following parameters are fixed and not varied between patients:
hbv_fixed_params = (
ini_V = exp10(-0.481),
ini_A = exp10(1.25),
r_T = 1.0,
r_E = 0.1,
r_X = 1.0,
n = 100,
ρ = 0.001,
ρ_o_ρ_r = 1.0e-5,
p_V = 100.0,
d_TI = 0.0039,
d_V = 0.67,
d_Q = 0.00385,
d_X = 0.2,
d_E = 0.01,
d_P = 0.239,
d_D = 1.66,
d_A = 0.47,
d_E_Q = 0.3,
T_max = 1.36e7,
ϕ_Q = 0.8,
ϕ_E = 100.0,
S_max = 1.0,
ϕ_S = 0.1473,
nS = 0.486,
I_threshold = 1.0e-4,
)The estimates of the remaining parameters:
hbv_estimated_params = (;
tv_log10_p_S = 7.643,
tv_log10_β = -4.358,
tv_log10_m = 1.618,
tv_log10_k_P = -3.968,
tv_log10_k_A = -4.869,
tv_log10_ϵ_NA = -1.485,
tv_log10_ϵ_IFN = -1.466,
tv_log10_r_E_IFN = 0.313,
tv_log10_conv_E = 2.745,
Ω = Diagonal([0.632^2, 2.489^2, 0.827^2, 1.292^2, 1.156^2, 1.583^2, 1.626^2, 2.739^2, 1.248^2]),
σ_add_log10_HBsAg = 0.0972,
σ_prop_log10_HBsAg = 2.2204e-16,
σ_add_log10_HBV = 0.3206,
σ_prop_log10_HBV = 0.0838,
σ_prop_log10_ALT = 0.0478,
σ_prop_log10_CD8 = 0.4149,
)Taken together, the full set of model parameters is
hbv_params = merge(hbv_fixed_params, hbv_estimated_params)6 Part 3: Understanding Treatment Mechanisms
6.1 Nucleoside Analog Effect
Nucleoside analogs (NAs) block viral production by inhibiting reverse transcriptase. During treatment with NAs (NA = true), \(\epsilon_{NA} < 1\) and hence the viral production is reduced:
\[V' = p_V \cdot \underbrace{\epsilon_{NA}}_{\text{treatment reduction}} \epsilon_{IFN} \cdot I - d_V \cdot V\]
6.2 Interferon Effect
Interferon (IFN) has dual mechanisms:
- Viral suppression (like NA):
- Reduces viral production via \(\epsilon_{IFN}\)
- Immune enhancement:
- Boosts effector T-cell killing via
r_E_IFN: \(\text{killing rate} = m \cdot \underbrace{r\_E\_IFN}_{\text{IFN boost}} \cdot E \cdot I\)
- Boosts effector T-cell killing via
6.3 Treatment Arms
The model supports four treatment scenarios:
| Arm | NA | IFN |
|---|---|---|
| Control (no treatment) | false | false |
| NA only | true | false |
| IFN only | false | true |
| NA + IFN combination | true | true |
7 Part 4: Basic Simulation
Let’s simulate a typical patient to see the natural disease progression. The model starts from a small viral inoculum and establishes chronic infection over 5 years, mimicking the clinical course from initial infection to chronic steady state.
7.1 Create a Subject
Create a subject with no treatment:
subject_no_tx = Subject(
id = "No Treatment",
covariates = (NA = false, IFN = false),
)Subject
ID: No Treatment
Covariates: NA, IFN
7.2 Simulate Natural History
sim_no_tx = simobs(
hbv_model,
subject_no_tx,
hbv_params,
zero_randeffs(hbv_model, subject_no_tx, hbv_params);
obstimes = 0:7:(5 * 365),
simulate_error = false
)
sim_df = DataFrame(sim_no_tx)7.3 Visualize Natural History
# Prepare data in long format for AoG
sim_long = @chain sim_df begin
@select :time :log10_HBV :log10_HBsAg :log10_ALT :log10_CD8
stack(_, Not("time"); variable_name = "biomarker")
@rsubset! isfinite(:value)
end
# Create plot with AoG
specs = data(sim_long) *
mapping(:time => (t -> t / 365) => "Time [year]", :value => "log₁₀ concentration") *
mapping(layout = :biomarker => renamer("log10_HBV" => "HBV DNA [copies/mL]", "log10_HBsAg" => "HBsAg [IU/mL]", "log10_ALT" => "ALT [U/L]", "log10_CD8" => "CD8+ T Cells [cells/mL]")) *
visual(Lines)
draw(specs; facet = (; linkyaxes = false))8 Part 5: Treatment Scenarios
8.1 Compare Treatment Scenarios
The key to meaningful treatment comparison is a multi-phase clinical protocol: the virus must first establish chronic infection before treatment begins.
- Untreated phase (5 years): Virus establishes chronic steady state
- NA suppression phase (4 years): Treatment with NAs
- Treatment phase (48 weeks): Arm-specific intervention
- Off-treatment phase (24 weeks): Assess durability of response
t_chronic = 5 * 365 # 5 years: establish chronic infection
t_na_suppressed = t_chronic + 4 * 365 # 4 years: NA treatment
t_eot = t_na_suppressed + 48 * 7 # end of 48-week treatment
t_end = t_eot + 24 * 7 # end of 24-week follow-up# Create subjects with time-varying covariates for each treatment arm
treatments = [
("No treatment", false, false),
("NA", true, false),
("IFN", false, true),
("NA + IFN", true, true),
]
pop = map(treatments) do (id, tx_NA, tx_IFN)
return Subject(;
id,
covariates_time = [0.0, t_chronic, t_na_suppressed, t_eot],
covariates = (
NA = [false, true, tx_NA, false], # treatment only during Phase 2
IFN = [false, false, tx_IFN, false],
),
)
end
# Simulate all arms for a "typical" patient (η = 0)
sims = simobs(
hbv_model,
pop,
hbv_params,
zero_randeffs(hbv_model, pop, hbv_params);
obstimes = t_na_suppressed:t_end,
simulate_error = false,
)
# Combine into dataframe
df_sims = stack(DataFrame(sims), ["log10_HBV", "log10_HBsAg", "log10_ALT", "log10_CD8"], ["time", "id"]; variable_name = "biomarker")
# Plot simulations
specs_data = data(df_sims) * mapping(
:time => (t -> (t - t_na_suppressed) / 7) => "Time after treatment start [week]",
color = :id => sorter("No treatment", "NA", "IFN", "NA + IFN") => "Treatment",
) * visual(Lines; color = (:blue, 0.2))
specs_refs = data(
[
(; value = 1.4, biomarker = "log10_HBV"),
(; value = log10(0.05), biomarker = "log10_HBsAg"),
]
) * mapping(
linestyle = direct("Functional cure") => "Threshold" => AlgebraOfGraphics.scale(:RefLines)
) * visual(HLines)
specs = (specs_data + specs_refs) * mapping(
:value => "log₁₀ concentration",
layout = :biomarker => renamer("log10_HBV" => "HBV DNA [copies/mL]", "log10_HBsAg" => "HBsAg [IU/mL]", "log10_ALT" => "ALT [U/L]", "log10_CD8" => "CD8+ T Cells [cells/mL]"),
)
draw(
specs,
scales(RefLines = (; palette = [:dash]));
axis = (; xticks = [0, 24, 48, 72]),
facet = (; linkxaxes = true, linkyaxes = false),
)8.2 Clinical Endpoints
Common clinical endpoints are:
HBsAg loss: HBsAg < 0.05 IU/mL for 24 weeks post-treatment.
Functional cure: Undetectable HBV DNA < 10^1.4 copies/mL and HBsAg < 0.05 IU/mL for 24 weeks post-treatment.
Notice that HBsAg barely declines during treatment and rebounds immediately when treatment stops. This is expected behavior — it reflects a key clinical reality of HBV therapy.
HBsAg production is not directly reduced by drugs. Looking at the ODE: \(S' = p_S \cdot I - d_V \cdot S\). Neither NA nor IFN appear in this equation. While HBsAg is always cleared at rate \(d_V\), its production (\(p_S \cdot I\)) depends entirely on the number of infected cells. For HBsAg to decline meaningfully, \(I\) itself must be reduced.
Infected cell clearance requires immune activation. \(I\) decreases through immune killing (\(m \cdot E \cdot I\)), but in chronic infection the effector T cells (\(E\)) are exhausted (high \(Q\) suppresses \(E\)). The typical patient’s immune system cannot clear \(I\) below the critical threshold (\(10^{-4}\)~cells/mL).
Functional cure requires the threshold switch. When \(I\) drops below \(10^{-4}\)~cells/mL, the model transitions to a “cured” state where viral and HBsAg production stops entirely. At population mean parameters, \(I\) never reaches this threshold — which is why the typical patient rebounds after treatment.
Only patients with favorable random effects achieve FC. Patients with stronger immune killing (higher \(m\)), better IFN response (higher \(r^{E}_{IFN}\)), and less exhaustion can push \(I\) below threshold during treatment. This matches clinical observations: FC rates are only ~5–15% with IFN-based regimens. We demonstrate this mechanism below.
9 Part 6: Population Variability Preview
Let’s see how parameter variability affects outcomes using the multi-phase protocol. We use the estimated IIV from the NLME fit to generate virtual patients and simulate 50 patients to capture the range of outcomes.
# Simulate 50 patients with IIV using the estimated Ω
sims = map(1:50) do id
subj = Subject(;
id,
covariates_time = [0.0, t_chronic, t_na_suppressed, t_eot],
covariates = (
NA = [false, true, true, false],
IFN = [false, false, true, false],
),
)
# Simulate with IIV:
# Sampling of random effects is repeated until random effects allow for successful simulation of the dynamics
randeffs = sample_randeffs(hbv_model, subj, hbv_params)
sim = simobs(
hbv_model, subj, hbv_params, randeffs;
obstimes = t_na_suppressed:t_end,
simulate_error = false
)
while !isvalid(sim)
randeffs = sample_randeffs(hbv_model, subj, hbv_params)
sim = simobs(
hbv_model, subj, hbv_params, randeffs;
obstimes = t_na_suppressed:t_end,
simulate_error = false
)
end
return sim
end
# Combine into dataframe
df_sims = stack(DataFrame(sims), ["log10_HBV", "log10_HBsAg", "log10_ALT", "log10_CD8"], ["time", "id"]; variable_name = "biomarker")
# Plot simulations
specs_data = data(df_sims) * mapping(
:time => (t -> (t - t_na_suppressed) / 7) => "Time after treatment start [week]",
group = :id,
) * visual(Lines; color = (:gray, 0.4))
specs_refs = data(
[
(; value = 1.4, biomarker = "log10_HBV"),
(; value = log10(0.05), biomarker = "log10_HBsAg"),
]
) * mapping(
linestyle = direct("Functional cure") => "Threshold" => AlgebraOfGraphics.scale(:RefLine),
) * visual(HLines)
specs = (specs_data + specs_refs) * mapping(
:value => "log₁₀ concentration",
layout = :biomarker => renamer("log10_HBV" => "HBV DNA [copies/mL]", "log10_HBsAg" => "HBsAg [IU/mL]", "log10_ALT" => "ALT [U/L]", "log10_CD8" => "CD8+ T Cells [cells/mL]"),
)
draw(
specs,
scales(RefLine = (; palette = [:dash]));
axis = (; xticks = [0, 24, 48, 72]),
facet = (; linkxaxes = true, linkyaxes = false),
legend = (; position = :bottom),
)9.1 Functional Cure Mechanism: Responder vs Non-Responder
To understand how functional cure works in this model, let’s compare two patients with NA+IFN treatment: the typical patient (population mean, η = 0) and a responder with favorable random effects (stronger immune killing and better IFN response).
The key state variable is infected cells (I): when I crosses below the threshold (\(10^{-4}\) cells/mL), the model switches to a “cured” state where viral and HBsAg production ceases permanently.
# Construct a typical patient and a responder
covariates_time = [0.0, t_chronic, t_na_suppressed, t_eot]
covariates = (NA = [false, true, true, false], IFN = [false, false, true, false])
# Typical patient: η = 0
typical_subject = Subject(; id = "Typical", covariates_time, covariates)
typical_randeffs = zero_randeffs(hbv_model, typical_subject, hbv_params)
# Responder: favorable random effects
# For instance, higher immune enhancement
responder_subject = Subject(; id = "Responder", covariates_time, covariates)
responder_randeffs = (; η = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0])
# Simulate both
sims = simobs(
hbv_model, [typical_subject, responder_subject], hbv_params,
[typical_randeffs, responder_randeffs];
obstimes = t_na_suppressed:t_end,
simulate_error = false,
)
# Combine into DataFrame
df_sims = @chain DataFrame(sims) begin
@rtransform! :log10_I = log10(:I)
stack(_, ["log10_I", "log10_HBV", "log10_HBsAg", "log10_ALT", "log10_CD8"], ["time", "id"]; variable_name = "biomarker")
@rsubset! isfinite(:value)
end
# Plot simulations
specs_data = data(df_sims) * mapping(
:time => (t -> (t - t_na_suppressed) / 7) => "Time after treatment start [week]",
color = :id => sorter("Typical", "Responder") => "Patient",
) * visual(Lines)
specs_refs = data(
[
(; value = 1.4, biomarker = "log10_HBV"),
(; value = log10(0.05), biomarker = "log10_HBsAg"),
(; value = -4, biomarker = "log10_I"),
]
) * mapping(
linestyle = AlgebraOfGraphics.direct(["Functional cure", "Functional cure", "Infected cells"]) => "Threshold" => AlgebraOfGraphics.scale(:RefLines)
) * visual(HLines)
specs = (specs_data + specs_refs) * mapping(
:value => "log₁₀ concentration",
layout = :biomarker => renamer("log10_I" => "Infected cells [cells/mL]", "log10_HBV" => "HBV DNA [copies/mL]", "log10_HBsAg" => "HBsAg [IU/mL]", "log10_ALT" => "ALT [U/L]", "log10_CD8" => "CD8+ T Cells [cells/mL]"),
)
draw(
specs,
scales(RefLines = (; palette = [:dash, :dot]));
axis = (; xticks = [0, 24, 48, 72]),
facet = (; linkxaxes = true, linkyaxes = false),
legend = (; position = :bottom, nbanks = 2),
)The responder achieves functional cure because their stronger immune response (higher \(m\) and \(r_{E,IFN}\)) drives infected cells below \(10^{-4}\) cells/mL. At that point, the model’s threshold switch activates:
- Viral production stops: \(V' = -d_V \cdot V\) (exponential decay)
- HBsAg production stops: \(S' = -d_V \cdot S\) (exponential decay)
- Immune activation ceases, preventing overshoot
This creates a stable cured equilibrium — even after treatment stops, the virus cannot re-establish because the immune system maintains control.
In a Virtual Clinical Trial (Tutorial 6), we sample thousands of virtual patients from the estimated population distribution to predict realistic FC rates.
10 Summary
10.1 Key Concepts
HBV infection is difficult to cure due to cccDNA persistence and immune exhaustion
The 11-ODE QSP model captures:
- Hepatocyte populations (T, I, R)
- Viral dynamics (V, S)
- Immune response (E, P, Q, X, D, A)
Treatment mechanisms:
- NA: Blocks viral production
- IFN: Enhances immune killing + reduces viral production
Clinical endpoints:
- HBsAg Loss: HBsAg < 0.05 IU/mL
- Functional Cure: HBsAg Loss + viral suppression (HBV DNA < 10^1.4 copies/mL)
10.2 Model Complexity Comparison
| Aspect | Tumor Burden | HBV QSP |
|---|---|---|
| State variables | 2 | 11 |
| Estimated parameters | 3 | 9 |
| Fixed parameters | 0 | 22 |
| Treatment covariates | 1 | 2 |
| Clinical endpoints | Response rate | FC, HBsAg Loss |
10.3 What’s Next?
In Tutorial 2: Global Sensitivity Analysis, we’ll identify which parameters most influence HBV treatment outcomes using Sobol and eFAST sensitivity analysis.