Repository navigation
Dosing algorithm #533
Description
This is inspired in part by the discussion in #388 where @ps2 and others noted that longer-tail exponential insulin absorption curves may expose some shortcomings of the dosing calculation, which is currently based on BG predicted at t=DIA in the future. Currently, the amount of bolus insulin suggested after a meal is entered is found as follows:

Such DIA-based dosing works well in many cases. However, it ignores the effect of the insulin delivered on BG between now (t=0) and DIA, which may result in too high or too low amount suggested. To address this issue, one may consider an iterative dosing algorithm that would search for an insulin dose so that BG predicted after the dose is delivered does not go below a target. Such iteration can in fact be captured by a relatively simple modification of the above dosing calculation:

The argument of the min function has the meaning of the amount of insulin required to drive BG to the target value at time t. The 1-IOB(t) term in the denominator represents the fraction of to-be-dosed insulin expended from 0 to time t. By choosing the minimum of the doses suggested over the time interval from t=0 to t=DIA we guarantee that the predicted BG, after the dose is delivered, will just touch the target value BGtarget at some point in time between now and DIA (not necessarily at DIA).
To illustrate how the suggested dynamic dosing calculation would work, consider a simple scenario: ISF=50, CR=10, DIA=6 hours, and meal of 50g that absorbs slowly at constant rate over a period of 5 hours. Since the entire meal is absorbed within DIA, the current dosing calculation would suggest 50/10 = 5 U bolus. A simple simulation shown below indicates that this dose is too large for the given meal, with BG dropping from initial 100 mg/dL to as low as 56 mg/dL (neglecting any low temping, which is not simulated here, but which would be insufficient to prevent the low)

In the same example, the dynamic dosing suggests 3.7 U, and the simulation shows that BG just touches the target (100 mg/dL) back at around 200min, which is t=140min after the dose is delivered.

Since this initial dose is insufficient to cover all carbs, BG eventually drifts up to a value above the target, but this would be corrected by Loop high-temping (which is not included in this simple calculation).
I think the suggested modification of the dosing algorithm could apply to both boluses and temps, and I think everything necessary to perform the calculation is available in DoseMath.
A concern is that the dynamic dosing described above may be too conservative, especially in view of the Loop dynamic carb absorption algorithm, which includes a 50% extension to a user-entered carb absorption time. The dynamic dosing formula could be modified to be more aggressive in at least two ways: by replacing BGtarget with BGmin (or perhaps even BGguard just for boluses), or by taking into account the effect future zero temping could have on predicted BG, thus enabling sort of dynamic super-bolusing as a part of the algorithm. The formula below includes both of these modifications:

Activity
elnjensen commentedon Jul 30, 2017
Interesting idea. I think I understand what you're suggesting here, but I have a question, which may be mostly about notation. Your
IOB(t)term doesn't actually have units of insulin, right? Rather, it's the fractional area under the insulin action curve at timet. So it's the ratio of the integral of that curve from 0 totto that of the integral from 0 toDIA. Is that correct?Edit: caught up on reading all the comments in #388 and I see that the usage there is indeed that
IOB(t)is unitless, descending from 1 to 0. So this all makes sense to me, given that definition.elnjensen commentedon Jul 31, 2017
Another thought on this: if you start out at or below target, then this formula will recommend a zero (or negative! 😉) bolus, since the minimum of the series will occur at the first time step. If you're sitting on a nice flat BG line just slightly below target, then zero bolus for a meal probably isn't what you want. So maybe it would make more sense to start that search for the minimum at some time > 0, though I'm not sure where, exactly. Maybe starting at/around the peak of the action curve (esp. if we parametrize the curves as suggested in #388, so this peak value is easily available)?
ps2 commentedon Jul 31, 2017
@elnjensen No, that's not correct. Bolus dosing allows full bolus unless min bg is below guard.
dm61 commentedon Jul 31, 2017
@elnjensen the formulas in my top post are just suggestions on how the core dosing calculation could be modified to a more general form beyond the current DIA-only based calculation; it would be necessary to add provisions to these formulas to account for various conditions, including pre-bolusing, starting form an initial bg below target, etc, just as is currently done in Loop. Many options could be considered, but would be good to stay away from ad-hoc conditions as much as possible. For example,
BGtargetin the formula above could actually beBGtarget(t)moving fromBGguardatt=0toBGtargetatt = DIA, which would allow the entire Loop logic to stay as is, including bolus recommendation allowed as long as bg is above guard, as noted by @ps2elnjensen commentedon Jul 31, 2017
@dm61 Right, that makes sense. @ps2 Sorry if I wasn't clear - I wasn't trying to describe current Loop behavior, but rather noting what would happen if the formula that @dm61 gives above were used, unmodified, to set bolus behavior. As @dm61 just noted in his most recent comment, there would have to be a more complicated set of conditions considered in order to use a dosing algorithm like this, one of which is the case I was noting of starting below target.
ps2 commentedon Aug 1, 2017
Thanks @dm61! This has been a potential issue for a long time (bolus for eventualBG results in near term low). I had been keeping it in the back of my mind, but I haven't taken issue, since we don't see it very often and when we do, it's very short term, and not much under target. With a faster peak insulin curve, it will be more pronounced, and I think there are already users who prebolus that probably see a deeper dip for some meals. (We're not great at pre-bolus).
My comment in that last ticket was partially a thinly veiled attempt to get you thinking about this. ;) The suggestion for an iterative approach was because that's all I could think of. I am wondering if there is a more direct way of computing this. Perhaps not. I'm guessing that any direct approach would be quite dependent on the actual carb absorption and insulin effect functions, and would break if we wanted to switch those out. So maybe iterative is still the best approach.
dm61 commentedon Aug 1, 2017
@ps2 the calculation suggested above is direct - it does not require iteration
ps2 commentedon Aug 2, 2017
Oh, I obviously misunderstood it. Now I see. And we can use BGpredicted sans insulin, and the IOB curve directly. Efficient and clever! Still convincing myself there aren't edge cases, but it sounds great. Also still considering the modifications for replacing target with the min of the range.
dm61 commentedon Aug 2, 2017
How about sliding the target value in that equation linearly from BGguard at t=0 to BGtarget at t=DIA? That would keep the existing Loop logic as is: it would allow bolus suggestion as long as the initial point is greater than BGguard, and it would ultimately drive BG to BGtarget just as Loop does now.
ps2 commentedon Aug 2, 2017
I've been playing with this, and so far it's looking good. A couple notes. At t = 0, we have a divide by zero; I can deal with that; it doesn't really make sense to look for a scale at that time. Second, I get an updated value of 3.0 when I run the equation, but I'm guessing you might have a slightly different insulin curve in operation.
https://github.com/ps2/LoopIOB/blob/master/Dosing%20algorithm%20%23533.ipynb
dm61 commentedon Aug 2, 2017
Yeah, calculation at zero does not make sense, start from the next element of the array, I'll correct those equations. I think my insulin curve was the same (td = DIA = 6h, tp=75min). Oh, I've just realized I had a typo in my meal example - it actually extended over 5 hours not 6; will edit that typo now. That might explain the difference 3.0 in your simulation versus my 3.7. edit just checked: I get 3.0 for the 6-hour meal.
jeremybarnum commentedon Aug 3, 2017
It's pretty clear to me this is the way forward. I think the next piece is a framework for safely taking into account "dry powder" from the ability to low temp in the back of the DIA, which allows for more aggressiveness in the front of the DIA. It should be something like "I am comfortable relying on being able to cut the basal in half, but I want to keep the rest for errors". Sort of a souped up version of low BG guard, integrated with the above.
dm61 commentedon Aug 6, 2017
I've played some more with the suggested dosing approach. This is a version that seems to work well:

where target
BGt(t)starts fromBGguardat time of dosing (t=0) and ends atBGtargetatt=DIA, while aggressiveness parameteralphablends in effects of potential future zero temping, thus allowing for a more aggressive bolus suggestion up front. As mentioned by @jeremybarnum,alpha = 0.5might be a default choice. Or, aggressivenessalphacould be another user-adjustable parameter.A Matlab script used to perform simulation and test various options can be found here: https://github.com/dm61/BasicLoopSimulator.
Below are couple of examples to illustrate typical differences between current DIA-based dosing and the above dynamic dosing.
Scenario 1:

50gmeal absorbed over5hours,ISF=50,CR=10,DIA=6,BGtarget=100,BGguard=70,20minpre-bolus. Dynamic dosing (right) delivers a smaller bolus, and BG never drops belowBGguardScenario 2:

50gmeal absorbed over2hours,ISF=50,CR=10,DIA=6,BGtarget=100,BGguard=70,alpha =0.5,20minpre-bolus. In this case, dynamic dosing safely delivers a larger bolus, which reduces the spike.jeremybarnum commentedon Aug 7, 2017
This looks fantastic @dm61. I'm looking forward to playing with it tomorrow and developing some intuition. Thanks for your efforts and clear explanations.
jeremybarnum commentedon Aug 8, 2017
Love the simulator! This one is awfully promising looking: Fiasp with no prebolus but aggressive setting:


And I wonder whether in reality the "true" carb absorption curve has some delay in it, which might not be that different from the Fiasp delay. Interesting to note this - Fiasp with 20 minute pre-bolus and aggressive alpha sends you uncomfortably low - even with fast burning carbs. When you buy it, they make a special call to tell you not to pre-bolus...
7 remaining items
ps2 commentedon Sep 2, 2017
@dm61 The difficulty we are having in implementing this is that we want to allow a certain threshold to avoid (suspend threshold), while targeting another separate target for eventual bg. I'm wondering if there is any way to implement a similar algorithm with that in mind.
dm61 commentedon Sep 2, 2017
@ps2 In the dosing equation of the top post I suggest you replace the fixed BGtarget with a function of time BGt(t) that slides from
suspend thresholdtoeventual targetoverDIA. This can be done in any number of ways. Two approaches I've tried in simple simulations are illustrated below. The first option is to just linearly move fromsuspendtoeventual. The second option is to stay atsuspend thresholduntil some fractionxofDIAand then linearly move toeventual target. The fractionxis somewhat arbitrary (could be0.5for example), or we could select it based on some fraction ofIOBexpired by that time. I like that second option better because it is less conservative, but still very safe.jeremybarnum commentedon Sep 5, 2017
Do we agree this approach should apply equally to bolusing and temp basal recommendations? I can imagine doing it for temp basals is a lower priority because for a range of reasons it seems less likely to create problematic lows. But in theory the framework should apply equally, so that people who want to run with very high max basal settings and basically operate in "unannounced" mode can benefit from this enhancement too. Again, in practice, it's probably an unimportant edge case, but I thought worth clarifying what everyone thinks the "ideal" state is.
trixing commentedon Sep 5, 2017
dm61 commentedon Sep 5, 2017
Applying this approach to temp basals would be fine but the benefits, if any, would likely be relatively small. Loop reevaluates temps every 5 minutes, and so it already has built-in corrective mechanisms, which do not exist for boluses. In basic idealized simulations, applying the same algorithm to temp basals results in smoother looking temp patterns, but the resulting bg responses look very similar to the responses with the existing Loop algorithm. With dynamic carbs, I've raised my temp limit considerably, and have not noticed any issues in actual practice. So, imo, applying this approach to temps would be a low priority.
scottleibrand commentedon Sep 5, 2017
This sounds like the kind of thing that would (only) be relevant if at some future time you decided to implement an SMB-like approach. At that point, having insulin requirements calculated independently of the delivery method (user-verified meal bolus, temp basals, or microboluses) would be very useful. But I don't think microboluses are even on the roadmap yet, so I'm guessing this will probably be a low priority until after things like automatic sensitivity detection are done. (Speaking only as an interested observer.)
ps2 commentedon Sep 6, 2017
Hey guys, it's already in the code and working.😜 On dev, both bolus and temp basals use this algo.
trixing commentedon Sep 6, 2017
scottleibrand commentedon Sep 6, 2017
If you could do arbitrarily short temp basals, there wouldn't be a difference. But because the minimum temp basal length on Medtronic pumps is 30m, and since we have to assume that we'll lose connectivity and any high temps will run to completion, we can only set high temps high enough to deliver the required insulin over 30 minutes. With microboluses, you can deliver the a sizeable fraction of the required insulin every 5m with each new BG, such that you can deliver all of it in 15-20m (up to twice as fast as with high temps) while still having the ability to stop if a sudden BG downtick indicates you maybe didn't need all the insulin you first thought.
I'll leave it at that to avoid rabbit-holing any further off-topic, but feel free to jump over to Gitter if you'd like to discuss further all the nuances of how we use SMBs in OpenAPS.
dm61 commentedon Sep 6, 2017
Oh, wow, @ps2, that's truly amazing 😲, I thought it only applied to bolus calculations in dev. Well, that wraps up this discussion I guess. 👏
rsilvers129 commentedon Sep 6, 2017
I know OpenAPS only sets temp basals that it calculates are ok to run to completion, but does Loop also do that? Or is Loop willing to set very high ones and then counts on being able to stop it five minutes later? If the latter, then just sending it as a bolus would seem better.
trixing commentedon Sep 6, 2017
COASTAL-1076 Support PumpManagerUI pausing onboarding (#533)
Merge pull request #1850 from LoopKit/tidepool-merge