Model integrated NCA

In addition to running NCA independently on a dataset, NCA parameters can be computed from the output of a simulation of a Pumas model.

Removed: the `@nca` macro

NCA used to be computed inside the model, by annotating the observed variables of interest with @nca in the @observed block. That macro has been removed, together with the conversion of a Subject to an NCASubject. Computing the NCA parameters from the simulation output instead, as shown below, keeps the model itself free of the analysis and gives access to the full set of NCA options.

NCA needs a route of administration, since the parameters that depend on the dose are undefined without it. The route is not part of the dose records, so it is carried as a covariate of the subject, holding one of the strings NCA accepts: "ivbolus", "ivinfusion" or "ev". A covariate named route is picked up by the reader automatically.

parmet = @model begin
    @param begin
        θ ∈ VectorDomain(5)
        Ω ∈ PDiagDomain(4)
        σ_prop ∈ RealDomain(; lower = 0)
    end

    @random begin
        η ~ MvNormal(Ω)
    end

    @pre begin
        CL = θ[1] * exp(η[1])
        Vc = θ[2] * exp(η[2])
        CLM = θ[3] * exp(η[3])
        VM = θ[4] * exp(η[4])
        fm = θ[5]
        K = (fm * (CL / Vc))
        KPM = ((1 - fm) * (CL / Vc))
        KM = (CLM / VM)
    end

    @dynamics begin
        Central' = -K * Central - KPM * Central
        Metabolite' = KPM * Central - KM * Metabolite
    end

    @derived begin
        cp := @. Central / Vc
        cm := @. Metabolite / VM
        dv_cp ~ @. ProportionalNormal(cp, σ_prop)
        dv_cm ~ @. ProportionalNormal(cm, σ_prop)
    end
end

ev = DosageRegimen(2000; ii = 24, addl = 3)
population =
    map(i -> Subject(; id = i, events = ev, covariates = (; route = "ivbolus")), 1:4)
parms = (;
    θ = [
        11.5, # CL
        50.0, # V
        10.0, # CLM
        8.0, # VM
        0.7, # fm
    ],
    Ω = diagm(0 => [0.04, 0.04, 0.04, 0.04]),
    σ_prop = 0.01,
)

The model is simulated as usual:

sims = simobs(parmet, population, parms; obstimes = 0:1:108)

The simulation is then written to a table and read_nca_pumas reads that table into NCA profiles. concentration names the column to analyse — here the simulated observation dv_cp, but any concentration column of the table can be used — and grouping by dosenum, the dose counter of the table, starts a new profile at every dose:

df = DataFrame(sims)
nca_dv_cp =
    read_nca_pumas(df; concentration = :dv_cp, dose = :amt, tau = :ii, group = [:dosenum])

The NCA parameters are computed from the profiles with run_nca:

run_nca(nca_dv_cp, [:auc, :cmax, :half_life])
16×11 DataFrame
Rowdosenumidprofiledoseroutedurationtauassumed_tauauccmaxhalf_life
Int64StringInt64Float64StringMissingMissingMissingFloat64Float64Float64
11112000.0ivbolusmissingmissingmissing191.58541.03133.27353
22122000.0ivbolusmissingmissingmissing192.25840.75983.27928
33132000.0ivbolusmissingmissingmissing191.82239.87673.27462
44142000.0ivbolusmissingmissingmissing192.76641.4313.27702
51252000.0ivbolusmissingmissingmissing204.38836.43513.85226
62262000.0ivbolusmissingmissingmissing208.02837.20943.8424
73272000.0ivbolusmissingmissingmissing208.38636.99233.85134
84282000.0ivbolusmissingmissingmissing207.41236.63883.84605
91392000.0ivbolusmissingmissingmissing168.39138.26073.01891
1023102000.0ivbolusmissingmissingmissing169.79639.29443.01361
1133112000.0ivbolusmissingmissingmissing169.02938.1393.02529
1243122000.0ivbolusmissingmissingmissing168.92438.42433.02407
1314132000.0ivbolusmissingmissingmissing202.05639.50393.5447
1424142000.0ivbolusmissingmissingmissing203.5739.47343.54055
1534152000.0ivbolusmissingmissingmissing204.14840.25353.54513
1644162000.0ivbolusmissingmissingmissing203.14939.3763.5479

The dosage regimen used here administers four doses, so there are four profiles and hence four rows per subject, identified by dosenum together with id. Observations taken before the first dose carry dosenum = 0 and have to be dropped before reading, since a profile needs exactly one dose record.

The endpoints to compute are given as the second argument, and they can be renamed and given their own keyword arguments there; see run_nca and the Endpoint Reference for the full set. Omitting the argument computes every endpoint applicable to the data.