Update pharmacokinetic parameters/calculations, add advanced settings, add disclaimer/citations, many improvements
This commit is contained in:
@@ -28,7 +28,12 @@ export const calculateCombinedProfile = (
|
||||
const timeStepHours = 0.25;
|
||||
const totalDays = days.length;
|
||||
const totalHours = totalDays * 24;
|
||||
const daysToSimulate = Math.min(parseInt(steadyStateConfig.daysOnMedication, 10) || 0, 5);
|
||||
|
||||
// Use steadyStateDays from advanced settings (allows 0 for "first day" simulation)
|
||||
const daysToSimulate = Math.min(
|
||||
parseInt(pkParams.advanced.steadyStateDays, 10) || 0,
|
||||
7 // cap at 7 days for performance
|
||||
);
|
||||
|
||||
// Convert days to processed doses with absolute time
|
||||
const allDoses: ProcessedDose[] = [];
|
||||
@@ -36,7 +41,7 @@ export const calculateCombinedProfile = (
|
||||
// Add steady-state doses (days before simulation period)
|
||||
// Use template day (first day) for steady state
|
||||
const templateDay = days[0];
|
||||
if (templateDay) {
|
||||
if (templateDay && daysToSimulate > 0) {
|
||||
for (let steadyDay = -daysToSimulate; steadyDay < 0; steadyDay++) {
|
||||
const dayOffsetMinutes = steadyDay * 24 * 60;
|
||||
templateDay.doses.forEach(dose => {
|
||||
|
||||
@@ -3,19 +3,22 @@
|
||||
*
|
||||
* Implements single-dose concentration calculations for lisdexamfetamine (LDX)
|
||||
* and its active metabolite dextroamphetamine (d-amph). Uses first-order
|
||||
* absorption and elimination kinetics.
|
||||
* absorption and elimination kinetics with optional advanced modifiers.
|
||||
*
|
||||
* @author Andreas Weyer
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { LDX_TO_DAMPH_CONVERSION_FACTOR, type PkParams } from '../constants/defaults';
|
||||
import { LDX_TO_DAMPH_SALT_FACTOR, DEFAULT_F_ORAL, type PkParams } from '../constants/defaults';
|
||||
|
||||
interface ConcentrationResult {
|
||||
ldx: number;
|
||||
damph: number;
|
||||
}
|
||||
|
||||
// Standard adult volume of distribution (Roberts et al. 2015): 377 L
|
||||
const STANDARD_VD_ADULT = 377.0;
|
||||
|
||||
// Pharmacokinetic calculations
|
||||
export const calculateSingleDoseConcentration = (
|
||||
dose: string,
|
||||
@@ -25,27 +28,61 @@ export const calculateSingleDoseConcentration = (
|
||||
const numDose = parseFloat(dose) || 0;
|
||||
if (timeSinceDoseHours < 0 || numDose <= 0) return { ldx: 0, damph: 0 };
|
||||
|
||||
const absorptionRate = parseFloat(pkParams.ldx.absorptionRate);
|
||||
// Extract base parameters
|
||||
const absorptionHalfLife = parseFloat(pkParams.ldx.absorptionHalfLife);
|
||||
const conversionHalfLife = parseFloat(pkParams.ldx.halfLife);
|
||||
const damphHalfLife = parseFloat(pkParams.damph.halfLife);
|
||||
|
||||
// Validate parameters to avoid division by zero or invalid calculations
|
||||
if (isNaN(absorptionRate) || absorptionRate <= 0 ||
|
||||
// Extract advanced parameters
|
||||
const fOral = parseFloat(pkParams.advanced.fOral) || DEFAULT_F_ORAL;
|
||||
const foodEnabled = pkParams.advanced.foodEffect.enabled;
|
||||
const tmaxDelay = foodEnabled ? parseFloat(pkParams.advanced.foodEffect.tmaxDelay) : 0;
|
||||
const urinePHEnabled = pkParams.advanced.urinePh.enabled;
|
||||
const phTendency = urinePHEnabled ? parseFloat(pkParams.advanced.urinePh.phTendency) : 6.0;
|
||||
|
||||
// Validate base parameters
|
||||
if (isNaN(absorptionHalfLife) || absorptionHalfLife <= 0 ||
|
||||
isNaN(conversionHalfLife) || conversionHalfLife <= 0 ||
|
||||
isNaN(damphHalfLife) || damphHalfLife <= 0) {
|
||||
return { ldx: 0, damph: 0 };
|
||||
}
|
||||
|
||||
const ka_ldx = Math.log(2) / absorptionRate;
|
||||
const k_conv = Math.log(2) / conversionHalfLife;
|
||||
const ke_damph = Math.log(2) / damphHalfLife;
|
||||
// Apply food effect: high-fat meal delays absorption by slowing rate (~+1h to Tmax)
|
||||
// Approximate by increasing absorption half-life proportionally
|
||||
const adjustedAbsorptionHL = absorptionHalfLife * (1 + (tmaxDelay / 1.5));
|
||||
|
||||
// Apply urine pH effect on elimination half-life
|
||||
// pH < 6: acidic (faster elimination, HL ~7-9h)
|
||||
// pH 6-7: normal (HL ~10-12h)
|
||||
// pH > 7: alkaline (slower elimination, HL ~13-15h up to 34h extreme)
|
||||
let adjustedDamphHL = damphHalfLife;
|
||||
if (urinePHEnabled) {
|
||||
if (phTendency < 6.0) {
|
||||
// Acidic: reduce HL by ~30%
|
||||
adjustedDamphHL = damphHalfLife * 0.7;
|
||||
} else if (phTendency > 7.5) {
|
||||
// Alkaline: increase HL by ~30-40%
|
||||
adjustedDamphHL = damphHalfLife * 1.35;
|
||||
}
|
||||
// else: normal pH 6-7.5, no adjustment
|
||||
}
|
||||
|
||||
// Calculate rate constants
|
||||
const ka_ldx = Math.log(2) / adjustedAbsorptionHL;
|
||||
const k_conv = Math.log(2) / conversionHalfLife;
|
||||
const ke_damph = Math.log(2) / adjustedDamphHL;
|
||||
|
||||
// Apply stoichiometric conversion and bioavailability
|
||||
const effectiveDose = numDose * LDX_TO_DAMPH_SALT_FACTOR * fOral;
|
||||
|
||||
// Calculate LDX concentration (prodrug)
|
||||
let ldxConcentration = 0;
|
||||
if (Math.abs(ka_ldx - k_conv) > 0.0001) {
|
||||
ldxConcentration = (numDose * ka_ldx / (ka_ldx - k_conv)) *
|
||||
(Math.exp(-k_conv * timeSinceDoseHours) - Math.exp(-ka_ldx * timeSinceDoseHours));
|
||||
}
|
||||
|
||||
// Calculate d-amphetamine concentration (active metabolite)
|
||||
let damphConcentration = 0;
|
||||
if (Math.abs(ka_ldx - ke_damph) > 0.0001 &&
|
||||
Math.abs(k_conv - ke_damph) > 0.0001 &&
|
||||
@@ -53,7 +90,20 @@ export const calculateSingleDoseConcentration = (
|
||||
const term1 = Math.exp(-ke_damph * timeSinceDoseHours) / ((ka_ldx - ke_damph) * (k_conv - ke_damph));
|
||||
const term2 = Math.exp(-k_conv * timeSinceDoseHours) / ((ka_ldx - k_conv) * (ke_damph - k_conv));
|
||||
const term3 = Math.exp(-ka_ldx * timeSinceDoseHours) / ((k_conv - ka_ldx) * (ke_damph - ka_ldx));
|
||||
damphConcentration = LDX_TO_DAMPH_CONVERSION_FACTOR * numDose * ka_ldx * k_conv * (term1 + term2 + term3);
|
||||
damphConcentration = effectiveDose * ka_ldx * k_conv * (term1 + term2 + term3);
|
||||
}
|
||||
|
||||
// Apply weight-based Vd scaling if enabled
|
||||
// Standard adult Vd = 377 L; weight-normalized ~5.4 L/kg
|
||||
// Concentration inversely proportional to Vd: C = Amount / Vd
|
||||
if (pkParams.advanced.weightBasedVd.enabled) {
|
||||
const bodyWeight = parseFloat(pkParams.advanced.weightBasedVd.bodyWeight);
|
||||
if (!isNaN(bodyWeight) && bodyWeight > 0) {
|
||||
const weightBasedVd = bodyWeight * 5.4; // L/kg factor from literature
|
||||
const scalingFactor = STANDARD_VD_ADULT / weightBasedVd;
|
||||
damphConcentration *= scalingFactor;
|
||||
ldxConcentration *= scalingFactor;
|
||||
}
|
||||
}
|
||||
|
||||
return { ldx: Math.max(0, ldxConcentration), damph: Math.max(0, damphConcentration) };
|
||||
|
||||
Reference in New Issue
Block a user