dynamic carb absorption #507
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
The previous carb absorption model in loop was based on a curve (Scheiner) that assumed that the rate of carb absorption would start out slow, and increase to a midpoint, and then taper off. As we are all well aware, in the real world, carb absorption is quite variable!
The changes in this branch attempt to improve on that model by updating the carb model as absorption is observed, and by taking a conservative view of how fast the remaining carbs will absorb.
Observing Carb Absorption
To make an attempt to see how carb absorption is actually progressing, we can take the observed changes in glucose, subtract changes modeled from insulin delivery, and look at the resulting difference, called insulin counteraction effect. See the counteractionEffects function for a version with less external coupling.
The effects represented by this term are more than just carb effects. It includes exercise, sensitivity changes, and even errors in insulin settings such as basal rate, ISF, etc. However, since the effect of consuming carbohydrates is relatively large, we can still make some useful adjustments to Loop's carb model by assuming that the effect is mainly carb absorption in the period following recorded meal entries.
The counteraction effect is a rate of glucose concentration change that can be converted into a carb amount by using the current insulin to carb ratio and the insulin sensitivity factor active at the time of the recorded meal entry. If multiple meal entries have active carbs on board, we can split up the estimated absorption between them using the minimum absorption rate (see below).
One of the most useful features of this update is that now you have a way to observe this effect term as well, and see it tracked against your carb entries, giving you new insights into how your food changes your blood sugar. This is available in the new carbohydrate effects screen.
In the top graph we can can see insulin counteraction effects graphed in green, and how it maps to carb absorption in darker green. Grey is drawn for future expected absorption, or absorption that has been clamped to minimum rate.
Below that, individual carb/meal entries are displayed:
The top row shows the meal as entered: carb amount, food type, date eating started, and absorption time.
Below that, Loop displays how many grams of the meal have been observed in insulin counteraction effect, and the current estimate of dynamic absorption time.
At the very bottom is a progress bar showing percentage of absorption completed, for both observed (green) and expired (grey). When the observed amount is less than the expired amount, the expired amount is used in computation.
Modeling Remaining Carbs On Board
Because carb absorption can be quite variable over time, we take a conservative view of how fast remaining carbs will absorb. If we assume they will absorb too fast, we will over treat, and if we assume to slow, we can under treat. In addition, if we observe very little, or no absorption at all, we still need a way to expire those carbs, to handle those situations where the meal was not really consumed, or was over estimated, or was masked by non-modeled effects like exercise.
So we compute a minimum absorption rate that is determined by taking the total number of carbs, and dividing by the entered duration of carb absorption plus 50%.
Remaining carbs are modeled to absorb at the minimum absorption rate linearly. So initially, you'll see that the estimated time is longer than you entered, and some carbs may be pushed beyond DIA, reducing your initial bolus recommendation. As carb absorption is observed, that estimate will update, sometimes drawing more carbs into treatment range (within DIA) and Loop will treat those by high temping. This is useful for handling larger meals, in effect mimicking a square wave bolus, but one that adapts based on absorption.
Testing Considerations
In the old carb model of Loop, if you saw faster than expected absorption at the beginning of the meal, Loop would assume that that rise was unrelated to carbs, and would treat you for that and also still assume that the bulk of carb absorption was going to happen, which could result in postprandial lows. Some of you may have dealt with that by weakening carb ratios, under-bolusing, reducing your max temp basal rate, or (best case) adjusting your CA times to very short.
The dynamic model will be more conservative in this case, so you may find your carb ratios need adjusting, or that (after some experience with the new model) you want to raise your max basal rate. As usual, pre-bolusing or setting a temp target before meals, may be needed to deal with meals that raise BG faster than a mealtime bolus can accommodate.
While CA duration is still part of recording meals in Loop, it is much less critical to get right. Now it serves as a way to tell Loop what kind of absorption to expect as a guideline, not as a rule.
Questions and Feedback
If you have questions about how this feature works, or about what you are experiencing while using the feature, gitter is probably your best place to ask and discuss. If you think you may have found a bug, please use the Issue Report feature in loop to capture relevant data surrounding the bug, and create a new ticket.
Enjoy!