Source code for pharmpy.modeling.allometry

from __future__ import annotations

from typing import Optional, Union

from pharmpy.basic import Expr, TExpr, TSymbol
from pharmpy.model import Assignment, Model, Parameter, Parameters

from .expressions import _create_symbol
from .odes import find_clearance_parameters, find_volume_parameters


[docs] def add_allometry( model: Model, allometric_variable: Optional[TSymbol] = None, reference_value: TExpr = 70, parameters: Optional[list[TExpr]] = None, initials: Optional[list[Union[int, float]]] = None, lower_bounds: Optional[list[Union[int, float]]] = None, upper_bounds: Optional[list[Union[int, float]]] = None, fixed: bool = True, ): """Add allometric scaling of parameters Add an allometric function to each listed parameter. The function will be P=P*(X/Z)**T where P is the parameter, X the allometric_variable, Z the reference_value and T is a theta. Default is to automatically use clearance and volume parameters. If there already exists a covariate effect (or allometric scaling) on a parameter with the specified allometric variable, nothing will be added. If no allometric variable is specified, it will be extracted from the dataset based on the descriptor "body weight". Parameters ---------- model : Model Pharmpy model allometric_variable : str or TExpr Value to use for allometry (X above) reference_value : str, int, float or TExpr Reference value (Z above) parameters : list Parameters to use or None (default) for all available CL, Q and V parameters initials : list Initial estimates for the exponents. Default is to use 0.75 for CL and Qs and 1 for Vs lower_bounds : list Lower bounds for the exponents. Default is 0 for all parameters upper_bounds : list Upper bounds for the exponents. Default is 2 for all parameters fixed : bool Whether the exponents should be fixed Return ------ Model Pharmpy model object Examples -------- >>> from pharmpy.modeling import load_example_model, add_allometry, remove_covariate_effect >>> model = load_example_model("pheno") >>> model = remove_covariate_effect(model, 'CL', 'WGT') >>> model = remove_covariate_effect(model, 'V', 'WGT') >>> model = add_allometry(model, allometric_variable='WGT') >>> model.statements.before_odes TVCL = POP_CL TVV = POP_VC ⎧TVV⋅(COVAPGR + 1) for APGR < 5 TVV = ⎩ TVV otherwise ETA_CL CL = TVCL⋅ℯ ALLO_CL ⎛WGT⎞ CL⋅⎜───⎟ CL = ⎝70 ⎠ ETA_VC VC = TVV⋅ℯ ALLO_VC ⎛WGT⎞ VC⋅⎜───⎟ VC = ⎝70 ⎠ V = VC S₁ = VC """ from pharmpy.modeling import has_covariate_effect if allometric_variable is None: try: allometric_variable = model.datainfo.descriptorix["body weight"][0].name except IndexError: raise ValueError( "No allometric variable could be found. Try setting the allometric_variable argument" ) variable = Expr(allometric_variable) reference = Expr(reference_value) parsed_parameters = [] if parameters is not None: parsed_parameters = [Expr(p) for p in parameters] if parameters is None or initials is None: cls = find_clearance_parameters(model) vcs = find_volume_parameters(model) if parameters is None: parsed_parameters = cls + vcs if initials is None: # Need to understand which parameter is CL or Q and which is V initials = [] for p in parsed_parameters: if p in cls: initials.append(0.75) elif p in vcs: initials.append(1.0) if not parsed_parameters: raise ValueError("No parameters provided") if lower_bounds is None: lower_bounds = [0.0] * len(parsed_parameters) if upper_bounds is None: upper_bounds = [2.0] * len(parsed_parameters) if not (len(parsed_parameters) == len(initials) == len(lower_bounds) == len(upper_bounds)): raise ValueError("The number of parameters, initials and bounds must be the same") sset = model.statements params = list(model.parameters) for p, init, lower, upper in zip(parsed_parameters, initials, lower_bounds, upper_bounds): if not has_covariate_effect(model, str(p), str(variable)): symb = _create_symbol( sset, params, model.random_variables, model.datainfo, f'ALLO_{p.name}', False ) param = Parameter(symb.name, init=init, lower=lower, upper=upper, fix=fixed) params.append(param) expr = p * (variable / reference) ** param.symbol new_ass = Assignment.create(p, expr) ind = sset.find_assignment_index(p) sset = sset[0 : ind + 1] + new_ass + sset[ind + 1 :] model = model.replace(statements=sset, parameters=Parameters.create(params)) model = model.update_source() return model