/** * Pharmacokinetic Model Tests * * Validates LDX/d-amphetamine concentration calculations against clinical data * from research literature. Tests cover: * - Clinical validation targets (Research Section 5.1) * - Age-specific elimination kinetics (Research Section 5.2) * - Renal function effects (Research Section 8.2) * - Edge cases and boundary conditions * * REFERENCES: * - AI Research Document (2026-01-17): Sections 3.2, 5.1, 5.2, 8.2 * - PMC4823324: Ermer et al. meta-analysis of LDX pharmacokinetics * - FDA NDA 021-977: Clinical pharmacology label * * @author Andreas Weyer * @license MIT */ import { calculateSingleDoseConcentration } from '../pharmacokinetics'; import { getDefaultState } from '../../constants/defaults'; import type { PkParams } from '../../constants/defaults'; // Helper: Get default PK parameters const getDefaultPkParams = (): PkParams => { return getDefaultState().pkParams; }; describe('Pharmacokinetic Model - Clinical Validation', () => { describe('70mg Reference Case (Research Section 5.1)', () => { test('LDX peak concentration should be ~55-65 ng/mL at 1h', () => { const pkParams = getDefaultPkParams(); const result = calculateSingleDoseConcentration('70', 1.0, pkParams); // Research target: ~58 ng/mL (±10%) expect(result.ldx).toBeGreaterThan(55); expect(result.ldx).toBeLessThan(65); }); test('d-Amphetamine peak concentration should be ~75-85 ng/mL at 4h', () => { const pkParams = getDefaultPkParams(); const result = calculateSingleDoseConcentration('70', 4.0, pkParams); // Research target: ~80 ng/mL (±10%) expect(result.damph).toBeGreaterThan(75); expect(result.damph).toBeLessThan(85); }); test('Crossover phenomenon: LDX peak < d-Amph peak', () => { const pkParams = getDefaultPkParams(); const ldxPeak = calculateSingleDoseConcentration('70', 1.0, pkParams); const damphPeak = calculateSingleDoseConcentration('70', 4.0, pkParams); // Characteristic prodrug behavior: prodrug peaks early but lower expect(ldxPeak.ldx).toBeLessThan(damphPeak.damph); }); test('LDX near-zero by 12h (rapid conversion)', () => { const pkParams = getDefaultPkParams(); const result = calculateSingleDoseConcentration('70', 12.0, pkParams); // LDX should be essentially eliminated (< 1 ng/mL) expect(result.ldx).toBeLessThan(1.0); }); test('d-Amphetamine persists at 12h (~25-35 ng/mL)', () => { const pkParams = getDefaultPkParams(); const result = calculateSingleDoseConcentration('70', 12.0, pkParams); // d-amph has 11h half-life, should still be measurable // ~80 ng/mL at 4h → ~30 ng/mL at 12h (roughly 1 half-life) expect(result.damph).toBeGreaterThan(25); expect(result.damph).toBeLessThan(35); }); }); describe('Age-Specific Elimination (Research Section 5.2)', () => { test('Child elimination: faster than adult (9h vs 11h half-life)', () => { const adultParams = getDefaultPkParams(); const childParams = { ...adultParams, advanced: { ...adultParams.advanced, ageGroup: { preset: 'child' as const } } }; const adultResult = calculateSingleDoseConcentration('70', 12.0, adultParams); const childResult = calculateSingleDoseConcentration('70', 12.0, childParams); // At 12h, child should have ~68% of adult concentration // exp(-ln(2)*12/9) / exp(-ln(2)*12/11) ≈ 0.68 const ratio = childResult.damph / adultResult.damph; expect(ratio).toBeGreaterThan(0.60); expect(ratio).toBeLessThan(0.75); }); test('Adult preset uses 11h half-life', () => { const pkParams = { ...getDefaultPkParams(), advanced: { ...getDefaultPkParams().advanced, ageGroup: { preset: 'adult' as const } } }; const result4h = calculateSingleDoseConcentration('70', 4.0, pkParams); const result15h = calculateSingleDoseConcentration('70', 15.0, pkParams); // At 15h (4h + 11h), concentration should be ~half of 4h peak // Allows some tolerance for absorption/distribution phase const ratio = result15h.damph / result4h.damph; expect(ratio).toBeGreaterThan(0.40); expect(ratio).toBeLessThan(0.60); }); test('Custom preset uses base half-life from config', () => { const customParams = { ...getDefaultPkParams(), damph: { halfLife: '13' }, // Custom 13h half-life advanced: { ...getDefaultPkParams().advanced, ageGroup: { preset: 'custom' as const } } }; const result4h = calculateSingleDoseConcentration('70', 4.0, customParams); const result17h = calculateSingleDoseConcentration('70', 17.0, customParams); // At 17h (4h + 13h), should be ~half of 4h peak const ratio = result17h.damph / result4h.damph; expect(ratio).toBeGreaterThan(0.40); expect(ratio).toBeLessThan(0.60); }); }); describe('Renal Function Effects (Research Section 8.2)', () => { test('Severe renal impairment: ~50% slower elimination', () => { const normalParams = getDefaultPkParams(); const renalParams = { ...normalParams, advanced: { ...normalParams.advanced, renalFunction: { enabled: true, severity: 'severe' as const } } }; const normalResult = calculateSingleDoseConcentration('70', 18.0, normalParams); const renalResult = calculateSingleDoseConcentration('70', 18.0, renalParams); // Severe renal: half-life 11h → 16.5h (1.5x factor) // At 18h, renal patient should have ~1.5x concentration vs normal const ratio = renalResult.damph / normalResult.damph; expect(ratio).toBeGreaterThan(1.3); expect(ratio).toBeLessThan(1.7); }); test('Normal/mild severity: no adjustment', () => { const baseParams = getDefaultPkParams(); const normalResult = calculateSingleDoseConcentration('70', 8.0, baseParams); const mildParams = { ...baseParams, advanced: { ...baseParams.advanced, renalFunction: { enabled: true, severity: 'mild' as const } } }; const mildResult = calculateSingleDoseConcentration('70', 8.0, mildParams); // Mild impairment should not affect elimination in this model expect(mildResult.damph).toBeCloseTo(normalResult.damph, 1); }); test('Renal function disabled: no effect', () => { const baseParams = getDefaultPkParams(); const disabledParams = { ...baseParams, advanced: { ...baseParams.advanced, renalFunction: { enabled: false, severity: 'severe' as const // Should be ignored when disabled } } }; const baseResult = calculateSingleDoseConcentration('70', 12.0, baseParams); const disabledResult = calculateSingleDoseConcentration('70', 12.0, disabledParams); expect(disabledResult.damph).toBeCloseTo(baseResult.damph, 1); }); }); describe('Edge Cases and Boundary Conditions', () => { test('Zero dose returns zero concentrations', () => { const pkParams = getDefaultPkParams(); const result = calculateSingleDoseConcentration('0', 4.0, pkParams); expect(result.ldx).toBe(0); expect(result.damph).toBe(0); }); test('Negative time returns zero concentrations', () => { const pkParams = getDefaultPkParams(); const result = calculateSingleDoseConcentration('70', -1.0, pkParams); expect(result.ldx).toBe(0); expect(result.damph).toBe(0); }); test('Very high dose scales proportionally', () => { const pkParams = getDefaultPkParams(); const result70 = calculateSingleDoseConcentration('70', 4.0, pkParams); const result140 = calculateSingleDoseConcentration('140', 4.0, pkParams); // Linear pharmacokinetics: 2x dose → 2x concentration expect(result140.damph).toBeCloseTo(result70.damph * 2, 0); }); test('Food effect delays absorption without changing AUC', () => { const pkParams = getDefaultPkParams(); const fedParams = { ...pkParams, advanced: { ...pkParams.advanced, foodEffect: { enabled: true, tmaxDelay: '1.0' // 1h delay } } }; // Peak should be later for fed state const fastedPeak1h = calculateSingleDoseConcentration('70', 1.0, pkParams); const fedPeak1h = calculateSingleDoseConcentration('70', 1.0, fedParams, true); const fedPeak2h = calculateSingleDoseConcentration('70', 2.0, fedParams, true); // Fed state at 1h should be lower than fasted (absorption delayed) expect(fedPeak1h.ldx).toBeLessThan(fastedPeak1h.ldx); // Fed peak should occur later (around 2h instead of 1h) expect(fedPeak2h.ldx).toBeGreaterThan(fedPeak1h.ldx); }); }); describe('Weight-Based Volume of Distribution', () => { test('Lower body weight increases concentrations', () => { const standardParams = getDefaultPkParams(); const lightweightParams = { ...standardParams, advanced: { ...standardParams.advanced, weightBasedVd: { enabled: true, bodyWeight: '50' // 50kg vs default ~70kg } } }; const standardResult = calculateSingleDoseConcentration('70', 4.0, standardParams); const lightweightResult = calculateSingleDoseConcentration('70', 4.0, lightweightParams); // Smaller Vd → higher concentration // 50kg: Vd ~270L, 70kg: Vd ~377L, ratio ~1.4x const ratio = lightweightResult.damph / standardResult.damph; expect(ratio).toBeGreaterThan(1.2); expect(ratio).toBeLessThan(1.6); }); test('Higher body weight decreases concentrations', () => { const standardParams = getDefaultPkParams(); const heavyweightParams = { ...standardParams, advanced: { ...standardParams.advanced, weightBasedVd: { enabled: true, bodyWeight: '100' // 100kg } } }; const standardResult = calculateSingleDoseConcentration('70', 4.0, standardParams); const heavyweightResult = calculateSingleDoseConcentration('70', 4.0, heavyweightParams); // Larger Vd → lower concentration const ratio = heavyweightResult.damph / standardResult.damph; expect(ratio).toBeLessThan(0.8); }); }); });