Skip to content

Dosing algorithm #533

Closed
Closed

Description

@dm61

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:
image
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:
image
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)
too_large_dosing_at_dia
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.
dynamic_dosing
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:
image

Activity

  1. elnjensen commented on Jul 30, 2017

    @elnjensen
    Contributor

    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 time t. So it's the ratio of the integral of that curve from 0 to t to that of the integral from 0 to DIA. 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.

  2. elnjensen commented on Jul 31, 2017

    @elnjensen
    Contributor

    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)?

  3. ps2 commented on Jul 31, 2017

    @ps2
    Collaborator

    @elnjensen No, that's not correct. Bolus dosing allows full bolus unless min bg is below guard.

  4. dm61 commented on Jul 31, 2017

    @dm61
    ContributorAuthor

    @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, BGtarget in the formula above could actually be BGtarget(t) moving from BGguard at t=0 to BGtarget at t = 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 @ps2

  5. elnjensen commented on Jul 31, 2017

    @elnjensen
    Contributor

    @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.

  6. ps2 commented on Aug 1, 2017

    @ps2
    Collaborator

    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.

  7. dm61 commented on Aug 1, 2017

    @dm61
    ContributorAuthor

    @ps2 the calculation suggested above is direct - it does not require iteration

  8. ps2 commented on Aug 2, 2017

    @ps2
    Collaborator

    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.

  9. dm61 commented on Aug 2, 2017

    @dm61
    ContributorAuthor

    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.

  10. ps2 commented on Aug 2, 2017

    @ps2
    Collaborator

    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

  11. dm61 commented on Aug 2, 2017

    @dm61
    ContributorAuthor

    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.

  12. jeremybarnum commented on Aug 3, 2017

    @jeremybarnum

    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.

  13. dm61 commented on Aug 6, 2017

    @dm61
    ContributorAuthor

    I've played some more with the suggested dosing approach. This is a version that seems to work well:
    image
    where target BGt(t) starts from BGguard at time of dosing (t=0) and ends at BGtarget at t=DIA, while aggressiveness parameter alpha blends in effects of potential future zero temping, thus allowing for a more aggressive bolus suggestion up front. As mentioned by @jeremybarnum, alpha = 0.5 might be a default choice. Or, aggressiveness alpha could 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: 50g meal absorbed over 5 hours, ISF=50, CR=10, DIA=6, BGtarget=100, BGguard=70, 20min pre-bolus. Dynamic dosing (right) delivers a smaller bolus, and BG never drops below BGguard
    5h_meal_08062017

    Scenario 2: 50g meal absorbed over 2 hours, ISF=50, CR=10, DIA=6, BGtarget=100, BGguard=70, alpha =0.5, 20min pre-bolus. In this case, dynamic dosing safely delivers a larger bolus, which reduces the spike.
    2h_meal_08062017

  14. jeremybarnum commented on Aug 7, 2017

    @jeremybarnum

    This looks fantastic @dm61. I'm looking forward to playing with it tomorrow and developing some intuition. Thanks for your efforts and clear explanations.

  15. jeremybarnum commented on Aug 8, 2017

    @jeremybarnum

    Love the simulator! This one is awfully promising looking: Fiasp with no prebolus but aggressive setting:
    screen shot 2017-08-08 at 1 39 21 am
    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...
    screen shot 2017-08-08 at 1 44 24 am

  16. 7 remaining items

  17. ps2 commented on Sep 2, 2017

    @ps2
    Collaborator

    @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.

  18. dm61 commented on Sep 2, 2017

    @dm61
    ContributorAuthor

    @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 threshold to eventual target over DIA. 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 from suspend to eventual. The second option is to stay at suspend threshold until some fraction x of DIA and then linearly move to eventual target. The fraction x is somewhat arbitrary (could be 0.5 for example), or we could select it based on some fraction of IOB expired by that time. I like that second option better because it is less conservative, but still very safe.

    bgt

  19. jeremybarnum commented on Sep 5, 2017

    @jeremybarnum

    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.

  20. trixing commented on Sep 5, 2017

    @trixing
  21. dm61 commented on Sep 5, 2017

    @dm61
    ContributorAuthor

    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.

  22. scottleibrand commented on Sep 5, 2017

    @scottleibrand

    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.)

  23. ps2 commented on Sep 6, 2017

    @ps2
    Collaborator

    Hey guys, it's already in the code and working.😜 On dev, both bolus and temp basals use this algo.

  24. trixing commented on Sep 6, 2017

    @trixing
  25. scottleibrand commented on Sep 6, 2017

    @scottleibrand

    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.

  26. dm61 commented on Sep 6, 2017

    @dm61
    ContributorAuthor

    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. 👏

  27. rsilvers129 commented on Sep 6, 2017

    @rsilvers129

    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.

  28. trixing commented on Sep 6, 2017

    @trixing
  29. added 2 commits that reference this issue on Nov 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions