| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- function iobCalc(treatment, time, curve, dia, peak, profile) {
- // iobCalc returns two variables:
- // activityContrib = units of treatment.insulin used in previous minute
- // iobContrib = units of treatment.insulin still remaining at a given point in time
- // ("Contrib" is used because these are the amounts contributed from pontentially multiple treatment.insulin dosages -- totals are calculated in total.js)
- //
- // Variables can be calculated using either:
- // A bilinear insulin action curve (which only takes duration of insulin activity (dia) as an input parameter) or
- // An exponential insulin action curve (which takes both a dia and a peak parameter)
- // (which functional form to use is specified in the user's profile)
- if (treatment.insulin) {
- // Calc minutes since bolus (minsAgo)
- if (typeof time === 'undefined') {
- time = new Date();
- }
- var bolusTime = new Date(treatment.date);
- var minsAgo = Math.round((time - bolusTime) / 1000 / 60);
- if (curve === 'bilinear') {
- return iobCalcBilinear(treatment, minsAgo, dia); // no user-specified peak with this model
- } else {
- return iobCalcExponential(treatment, minsAgo, dia, peak, profile);
- }
- } else { // empty return if (treatment.insulin) == False
- return {};
- }
- }
- function iobCalcBilinear(treatment, minsAgo, dia) {
-
- var default_dia = 3.0 // assumed duration of insulin activity, in hours
- var peak = 75; // assumed peak insulin activity, in minutes
- var end = 180; // assumed end of insulin activity, in minutes
- // Scale minsAgo by the ratio of the default dia / the user's dia
- // so the calculations for activityContrib and iobContrib work for
- // other dia values (while using the constants specified above)
- var timeScalar = default_dia / dia;
- var scaled_minsAgo = timeScalar * minsAgo;
- var activityContrib = 0;
- var iobContrib = 0;
- // Calc percent of insulin activity at peak, and slopes up to and down from peak
- // Based on area of triangle, because area under the insulin action "curve" must sum to 1
- // (length * height) / 2 = area of triangle (1), therefore height (activityPeak) = 2 / length (which in this case is dia, in minutes)
- // activityPeak scales based on user's dia even though peak and end remain fixed
- var activityPeak = 2 / (dia * 60)
- var slopeUp = activityPeak / peak
- var slopeDown = -1 * (activityPeak / (end - peak))
- if (scaled_minsAgo < peak) {
- activityContrib = treatment.insulin * (slopeUp * scaled_minsAgo);
- var x1 = (scaled_minsAgo / 5) + 1; // scaled minutes since bolus, pre-peak; divided by 5 to work with coefficients estimated based on 5 minute increments
- iobContrib = treatment.insulin * ( (-0.001852*x1*x1) + (0.001852*x1) + 1.000000 );
- } else if (scaled_minsAgo < end) {
-
- var minsPastPeak = scaled_minsAgo - peak
- activityContrib = treatment.insulin * (activityPeak + (slopeDown * minsPastPeak));
- var x2 = ((scaled_minsAgo - peak) / 5); // scaled minutes past peak; divided by 5 to work with coefficients estimated based on 5 minute increments
- iobContrib = treatment.insulin * ( (0.001323*x2*x2) + (-0.054233*x2) + 0.555560 );
- }
- return {
- activityContrib: activityContrib,
- iobContrib: iobContrib
- };
- }
- function iobCalcExponential(treatment, minsAgo, dia, peak, profile) {
- // Use custom peak time (in minutes) if value is valid
- if ( profile.curve === "rapid-acting" ) {
- if (profile.useCustomPeakTime === true && profile.insulinPeakTime !== undefined) {
- if ( profile.insulinPeakTime > 120 ) {
- console.error('Setting maximum Insulin Peak Time of 120m for',profile.curve,'insulin');
- peak = 120;
- } else if ( profile.insulinPeakTime < 50 ) {
- console.error('Setting minimum Insulin Peak Time of 50m for',profile.curve,'insulin');
- peak = 50;
- } else {
- peak = profile.insulinPeakTime;
- }
- } else {
- peak = 75;
- }
- } else if ( profile.curve === "ultra-rapid" ) {
- if (profile.useCustomPeakTime === true && profile.insulinPeakTime !== undefined) {
- if ( profile.insulinPeakTime > 100 ) {
- console.error('Setting maximum Insulin Peak Time of 100m for',profile.curve,'insulin');
- peak = 100;
- } else if ( profile.insulinPeakTime < 35 ) {
- console.error('Setting minimum Insulin Peak Time of 35m for',profile.curve,'insulin');
- peak = 35;
- } else {
- peak = profile.insulinPeakTime;
- }
- } else {
- peak = 55;
- }
- } else {
- console.error('Curve of',profile.curve,'is not supported.');
- }
- var end = dia * 60; // end of insulin activity, in minutes
- var activityContrib = 0;
- var iobContrib = 0;
- if (minsAgo < end) {
-
- // Formula source: https://github.com/LoopKit/Loop/issues/388#issuecomment-317938473
- // Mapping of original source variable names to those used here:
- // td = end
- // tp = peak
- // t = minsAgo
- var tau = peak * (1 - peak / end) / (1 - 2 * peak / end); // time constant of exponential decay
- var a = 2 * tau / end; // rise time factor
- var S = 1 / (1 - a + (1 + a) * Math.exp(-end / tau)); // auxiliary scale factor
-
- activityContrib = treatment.insulin * (S / Math.pow(tau, 2)) * minsAgo * (1 - minsAgo / end) * Math.exp(-minsAgo / tau);
- iobContrib = treatment.insulin * (1 - S * (1 - a) * ((Math.pow(minsAgo, 2) / (tau * end * (1 - a)) - minsAgo / tau - 1) * Math.exp(-minsAgo / tau) + 1));
- //console.error('DIA: ' + dia + ' minsAgo: ' + minsAgo + ' end: ' + end + ' peak: ' + peak + ' tau: ' + tau + ' a: ' + a + ' S: ' + S + ' activityContrib: ' + activityContrib + ' iobContrib: ' + iobContrib);
- }
- return {
- activityContrib: activityContrib,
- iobContrib: iobContrib
- };
- }
- exports = module.exports = iobCalc;
|