calculate.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. function iobCalc(treatment, time, curve, dia, peak, profile) {
  2. // iobCalc returns two variables:
  3. // activityContrib = units of treatment.insulin used in previous minute
  4. // iobContrib = units of treatment.insulin still remaining at a given point in time
  5. // ("Contrib" is used because these are the amounts contributed from pontentially multiple treatment.insulin dosages -- totals are calculated in total.js)
  6. //
  7. // Variables can be calculated using either:
  8. // A bilinear insulin action curve (which only takes duration of insulin activity (dia) as an input parameter) or
  9. // An exponential insulin action curve (which takes both a dia and a peak parameter)
  10. // (which functional form to use is specified in the user's profile)
  11. if (treatment.insulin) {
  12. // Calc minutes since bolus (minsAgo)
  13. if (typeof time === 'undefined') {
  14. time = new Date();
  15. }
  16. var bolusTime = new Date(treatment.date);
  17. var minsAgo = Math.round((time - bolusTime) / 1000 / 60);
  18. if (curve === 'bilinear') {
  19. return iobCalcBilinear(treatment, minsAgo, dia); // no user-specified peak with this model
  20. } else {
  21. return iobCalcExponential(treatment, minsAgo, dia, peak, profile);
  22. }
  23. } else { // empty return if (treatment.insulin) == False
  24. return {};
  25. }
  26. }
  27. function iobCalcBilinear(treatment, minsAgo, dia) {
  28. var default_dia = 3.0 // assumed duration of insulin activity, in hours
  29. var peak = 75; // assumed peak insulin activity, in minutes
  30. var end = 180; // assumed end of insulin activity, in minutes
  31. // Scale minsAgo by the ratio of the default dia / the user's dia
  32. // so the calculations for activityContrib and iobContrib work for
  33. // other dia values (while using the constants specified above)
  34. var timeScalar = default_dia / dia;
  35. var scaled_minsAgo = timeScalar * minsAgo;
  36. var activityContrib = 0;
  37. var iobContrib = 0;
  38. // Calc percent of insulin activity at peak, and slopes up to and down from peak
  39. // Based on area of triangle, because area under the insulin action "curve" must sum to 1
  40. // (length * height) / 2 = area of triangle (1), therefore height (activityPeak) = 2 / length (which in this case is dia, in minutes)
  41. // activityPeak scales based on user's dia even though peak and end remain fixed
  42. var activityPeak = 2 / (dia * 60)
  43. var slopeUp = activityPeak / peak
  44. var slopeDown = -1 * (activityPeak / (end - peak))
  45. if (scaled_minsAgo < peak) {
  46. activityContrib = treatment.insulin * (slopeUp * scaled_minsAgo);
  47. 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
  48. iobContrib = treatment.insulin * ( (-0.001852*x1*x1) + (0.001852*x1) + 1.000000 );
  49. } else if (scaled_minsAgo < end) {
  50. var minsPastPeak = scaled_minsAgo - peak
  51. activityContrib = treatment.insulin * (activityPeak + (slopeDown * minsPastPeak));
  52. var x2 = ((scaled_minsAgo - peak) / 5); // scaled minutes past peak; divided by 5 to work with coefficients estimated based on 5 minute increments
  53. iobContrib = treatment.insulin * ( (0.001323*x2*x2) + (-0.054233*x2) + 0.555560 );
  54. }
  55. return {
  56. activityContrib: activityContrib,
  57. iobContrib: iobContrib
  58. };
  59. }
  60. function iobCalcExponential(treatment, minsAgo, dia, peak, profile) {
  61. // Use custom peak time (in minutes) if value is valid
  62. if ( profile.curve === "rapid-acting" ) {
  63. if (profile.useCustomPeakTime === true && profile.insulinPeakTime !== undefined) {
  64. if ( profile.insulinPeakTime > 120 ) {
  65. console.error('Setting maximum Insulin Peak Time of 120m for',profile.curve,'insulin');
  66. peak = 120;
  67. } else if ( profile.insulinPeakTime < 50 ) {
  68. console.error('Setting minimum Insulin Peak Time of 50m for',profile.curve,'insulin');
  69. peak = 50;
  70. } else {
  71. peak = profile.insulinPeakTime;
  72. }
  73. } else {
  74. peak = 75;
  75. }
  76. } else if ( profile.curve === "ultra-rapid" ) {
  77. if (profile.useCustomPeakTime === true && profile.insulinPeakTime !== undefined) {
  78. if ( profile.insulinPeakTime > 100 ) {
  79. console.error('Setting maximum Insulin Peak Time of 100m for',profile.curve,'insulin');
  80. peak = 100;
  81. } else if ( profile.insulinPeakTime < 35 ) {
  82. console.error('Setting minimum Insulin Peak Time of 35m for',profile.curve,'insulin');
  83. peak = 35;
  84. } else {
  85. peak = profile.insulinPeakTime;
  86. }
  87. } else {
  88. peak = 55;
  89. }
  90. } else {
  91. console.error('Curve of',profile.curve,'is not supported.');
  92. }
  93. var end = dia * 60; // end of insulin activity, in minutes
  94. var activityContrib = 0;
  95. var iobContrib = 0;
  96. if (minsAgo < end) {
  97. // Formula source: https://github.com/LoopKit/Loop/issues/388#issuecomment-317938473
  98. // Mapping of original source variable names to those used here:
  99. // td = end
  100. // tp = peak
  101. // t = minsAgo
  102. var tau = peak * (1 - peak / end) / (1 - 2 * peak / end); // time constant of exponential decay
  103. var a = 2 * tau / end; // rise time factor
  104. var S = 1 / (1 - a + (1 + a) * Math.exp(-end / tau)); // auxiliary scale factor
  105. activityContrib = treatment.insulin * (S / Math.pow(tau, 2)) * minsAgo * (1 - minsAgo / end) * Math.exp(-minsAgo / tau);
  106. iobContrib = treatment.insulin * (1 - S * (1 - a) * ((Math.pow(minsAgo, 2) / (tau * end * (1 - a)) - minsAgo / tau - 1) * Math.exp(-minsAgo / tau) + 1));
  107. //console.error('DIA: ' + dia + ' minsAgo: ' + minsAgo + ' end: ' + end + ' peak: ' + peak + ' tau: ' + tau + ' a: ' + a + ' S: ' + S + ' activityContrib: ' + activityContrib + ' iobContrib: ' + iobContrib);
  108. }
  109. return {
  110. activityContrib: activityContrib,
  111. iobContrib: iobContrib
  112. };
  113. }
  114. exports = module.exports = iobCalc;