88 lines
3.1 KiB
TypeScript
88 lines
3.1 KiB
TypeScript
import { timeToMinutes } from './timeUtils';
|
|
import { calculateSingleDoseConcentration } from './pharmacokinetics';
|
|
import type { Dose, Deviation, SteadyStateConfig, PkParams, ConcentrationPoint } from '../constants/defaults';
|
|
|
|
interface DoseWithTime extends Omit<Dose, 'time'> {
|
|
time: number;
|
|
isPlan?: boolean;
|
|
}
|
|
|
|
export const calculateCombinedProfile = (
|
|
doseSchedule: Dose[],
|
|
deviationList: Deviation[] = [],
|
|
correction: Deviation | null = null,
|
|
steadyStateConfig: SteadyStateConfig,
|
|
simulationDays: string,
|
|
pkParams: PkParams
|
|
): ConcentrationPoint[] => {
|
|
const dataPoints: ConcentrationPoint[] = [];
|
|
const timeStepHours = 0.25;
|
|
const totalHours = (parseInt(simulationDays, 10) || 3) * 24;
|
|
const daysToSimulate = Math.min(parseInt(steadyStateConfig.daysOnMedication, 10) || 0, 5);
|
|
|
|
for (let t = 0; t <= totalHours; t += timeStepHours) {
|
|
let totalLdx = 0;
|
|
let totalDamph = 0;
|
|
const allDoses: DoseWithTime[] = [];
|
|
|
|
const maxDayOffset = (parseInt(simulationDays, 10) || 3) - 1;
|
|
|
|
for (let day = -daysToSimulate; day <= maxDayOffset; day++) {
|
|
const dayOffset = day * 24 * 60;
|
|
doseSchedule.forEach(d => {
|
|
// Skip doses with empty or invalid time values
|
|
const timeStr = String(d.time || '').trim();
|
|
const doseStr = String(d.dose || '').trim();
|
|
const doseNum = parseFloat(doseStr);
|
|
|
|
if (!timeStr || timeStr === '' || !doseStr || doseStr === '' || doseNum === 0 || isNaN(doseNum)) {
|
|
return;
|
|
}
|
|
allDoses.push({ ...d, time: timeToMinutes(d.time) + dayOffset, isPlan: true });
|
|
});
|
|
}
|
|
|
|
const currentDeviations = [...deviationList];
|
|
if (correction) {
|
|
currentDeviations.push({ ...correction, isAdditional: true });
|
|
}
|
|
|
|
currentDeviations.forEach(dev => {
|
|
// Skip deviations with empty or invalid time values
|
|
const timeStr = String(dev.time || '').trim();
|
|
const doseStr = String(dev.dose || '').trim();
|
|
const doseNum = parseFloat(doseStr);
|
|
|
|
if (!timeStr || timeStr === '' || !doseStr || doseStr === '' || doseNum === 0 || isNaN(doseNum)) {
|
|
return;
|
|
}
|
|
const devTime = timeToMinutes(dev.time) + (dev.dayOffset || 0) * 24 * 60;
|
|
if (!dev.isAdditional) {
|
|
const closestDoseIndex = allDoses.reduce((closest, dose, index) => {
|
|
if (!dose.isPlan) return closest;
|
|
const diff = Math.abs(dose.time - devTime);
|
|
if (diff <= 60 && diff < closest.minDiff) {
|
|
return { index, minDiff: diff };
|
|
}
|
|
return closest;
|
|
}, { index: -1, minDiff: 61 }).index;
|
|
if (closestDoseIndex !== -1) {
|
|
allDoses.splice(closestDoseIndex, 1);
|
|
}
|
|
}
|
|
allDoses.push({ ...dev, time: devTime });
|
|
});
|
|
|
|
allDoses.forEach(doseInfo => {
|
|
const timeSinceDoseHours = t - doseInfo.time / 60;
|
|
const concentrations = calculateSingleDoseConcentration(doseInfo.dose, timeSinceDoseHours, pkParams);
|
|
totalLdx += concentrations.ldx;
|
|
totalDamph += concentrations.damph;
|
|
});
|
|
|
|
dataPoints.push({ timeHours: t, ldx: totalLdx, damph: totalDamph });
|
|
}
|
|
|
|
return dataPoints;
|
|
};
|