Source code for pharmpy.modeling.basic_models

"""
:meta private:
"""

from __future__ import annotations

from pathlib import Path
from typing import Optional, Union

from pharmpy.basic import Expr
from pharmpy.deps import sympy
from pharmpy.internals.fs.path import normalize_user_given_path
from pharmpy.model import (
    Assignment,
    ColumnInfo,
    Compartment,
    CompartmentalSystem,
    CompartmentalSystemBuilder,
    DataInfo,
    EstimationStep,
    ExecutionSteps,
    Model,
    NormalDistribution,
    Parameter,
    Parameters,
    RandomVariables,
    Statements,
    get_and_check_odes,
    output,
)

from .data import add_admid, create_default_datainfo, read_dataset_from_datainfo
from .error import set_proportional_error_model
from .odes import add_bioavailability, set_first_order_absorption
from .parameter_variability import add_iiv, create_joint_distribution
from .parameters import set_initial_estimates


[docs] def create_basic_pk_model( administration: str = 'iv', dataset_path: Optional[Union[str, Path]] = None, cl_init: float = 0.01, vc_init: float = 1.0, mat_init: float = 0.1, ) -> Model: """ Creates a basic pk model of given type. The model will be a one compartment model, with first order elimination and in the case of oral administration first order absorption with no absorption delay. The elimination rate will be :math:`CL/V` and the absorption rate will be :math:`1/MAT` Parameters ---------- administration : str Type of PK model to create. Supported are 'iv', 'oral' and 'ivoral' dataset_path : str or Path Optional path to a dataset cl_init : float Initial estimate of the clearance parameter vc_init : float Initial estimate of the central volume parameter mat_init : float Initial estimate of the mean absorption time parameter (if applicable) Return ------ Model Pharmpy model object Examples -------- >>> from pharmpy.modeling import * >>> model = create_basic_pk_model('oral') """ if dataset_path is not None: dataset_path = normalize_user_given_path(dataset_path) di = create_default_datainfo(dataset_path) df = read_dataset_from_datainfo(di, datatype='nonmem') else: di_col_dict = {'ID': 'id', 'TIME': 'idv', 'AMT': 'dose', 'DV': 'dv'} di_ci = [ColumnInfo.create(key, type=value) for key, value in di_col_dict.items()] di = DataInfo.create(di_ci) df = None if administration not in [ 'iv', 'oral', 'ivoral', ]: raise ValueError(f'Invalid input: `{administration}` as administration is not supported.') pop_cl = Parameter('POP_CL', cl_init, lower=0) pop_vc = Parameter('POP_VC', vc_init, lower=0) iiv_cl = Parameter('IIV_CL', 0.1) iiv_vc = Parameter('IIV_VC', 0.1) params = Parameters((pop_cl, pop_vc, iiv_cl, iiv_vc)) eta_cl_name = 'ETA_CL' eta_cl = NormalDistribution.create(eta_cl_name, 'iiv', 0, iiv_cl.symbol) eta_vc_name = 'ETA_VC' eta_vc = NormalDistribution.create(eta_vc_name, 'iiv', 0, iiv_vc.symbol) rvs = RandomVariables.create([eta_cl, eta_vc]) CL = Expr.symbol('CL') VC = Expr.symbol('VC') cl_ass = Assignment(CL, pop_cl.symbol * Expr.symbol(eta_cl_name).exp()) vc_ass = Assignment(VC, pop_vc.symbol * Expr.symbol(eta_vc_name).exp()) cb = CompartmentalSystemBuilder() # FIXME: This shouldn't be used here from pharmpy.model.external.nonmem.advan import dosing, find_dose doses = dosing(di, df, 1) central = Compartment.create('CENTRAL', doses=find_dose(doses, 1)) cb.add_compartment(central) cb.add_flow(central, output, CL / VC) ipred = Assignment(Expr.symbol('IPRED'), central.amount / VC) y_ass = Assignment(Expr.symbol('Y'), ipred.symbol) stats = Statements([cl_ass, vc_ass, CompartmentalSystem(cb), ipred, y_ass]) est = EstimationStep.create( "FOCE", interaction=True, maximum_evaluations=99999, predictions=('PRED', 'CIPREDI'), residuals=('CWRES',), ) eststeps = ExecutionSteps.create([est]) model = Model.create( name='start', statements=stats, execution_steps=eststeps, dependent_variables={y_ass.symbol: 1}, random_variables=rvs, parameters=params, description='Start model', dataset=df, datainfo=di, ) model = set_proportional_error_model(model) model = create_joint_distribution(model, [eta_cl_name, eta_vc_name]) if administration == 'oral' or administration == 'ivoral': model = set_first_order_absorption(model) model = set_initial_estimates(model, {'POP_MAT': mat_init}) model = add_iiv(model, list_of_parameters='MAT', expression='exp', initial_estimate=0.1) if administration == 'ivoral': # FIXME: Dependent on CMT column having 1 and 2 as values otherwise # compartment structure don't match model = add_bioavailability(model, logit_transform=True) model = set_initial_estimates(model, {'POP_BIO': 0.5}) # Add IIV to BIO # TODO: ? Should there be another initial estimate? model = add_iiv(model, list_of_parameters="BIO", expression='add', initial_estimate=0.1) # Set dosing to the CENTRAL compartment as well ode = model.statements.ode_system cb = CompartmentalSystemBuilder(ode) if df is None: doses = dosing(di, df, 2) central_dose = find_dose(doses, comp_number=2, admid=2) else: # doses = dosing(di, df, 1) central_dose = find_dose(doses, comp_number=2, admid=2) if not central_dose: raise ValueError( ( "Could not determine IV dose from dataset. " "Currently require CMT column with values 1 and 2 " ) ) central = cb.find_compartment("CENTRAL") assert central is not None cb.set_dose(central, dose=central_dose) ode = CompartmentalSystem(cb) model = model.replace( statements=model.statements.before_odes + ode + model.statements.after_odes ) if df is not None: model = add_admid(model) # Change bioavailability to piecewise for oral/iv doses model = model.replace( statements=model.statements.reassign( Expr.symbol("F_BIO"), Expr.piecewise( ( 1 / (1 + ((-Expr.symbol("BIO")).exp())), sympy.Eq(sympy.Symbol('ADMID'), 1), ), (1, sympy.true), ), ) ) # Add covariate to error model with the following logic # RUV = 1 * covariate # Y = F + F*EPS*RUV ruv_ass = Assignment(Expr.symbol("RUV"), Expr.integer(1)) model = model.replace( statements=model.statements.before_odes + ruv_ass + get_and_check_odes(model) + model.statements.after_odes ) Y_ass = model.statements.find_assignment("Y") assert Y_ass is not None ipred = Y_ass.expression.make_args(Y_ass.expression)[0] error = Y_ass.expression.make_args(Y_ass.expression)[1] new_Y_ass = Assignment.create(Y_ass.symbol, ipred + error * ruv_ass.symbol) model = model.replace( statements=model.statements.reassign(Y_ass.symbol, new_Y_ass.expression) ) return model