Skip to content

Instantly share code, notes, and snippets.

@scottleibrand
Last active January 11, 2016 17:18
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save scottleibrand/df1019f8e12bac9030dc to your computer and use it in GitHub Desktop.

#Proposed oref0 Meal Assist algorithm

Currently the oref0 OpenAPS implementation is designed to completely ignore carbs, on the assumption that carb entry will not be reliable, either in frequency or accuracy. However, when carb information is available from Bolus Wizard entries or from Nightscout, it seems we can do better than that, by using carb information as just one signal to decide whether it's safe to high-temp when the oref0 bolus snooze algorithm would otherwise choose to do nothing (and require the user to manually administer the remainder of their meal bolus as needed.

Below are some design goals and a proposed algorithm that should accomplish this. Feedback welcome!

#Design goals:

  • Assist with partial meal boluses if user enters carb amount
  • High-temp early but safely while BG is still rising
  • Do not require any assumptions about carb absorption profile
  • Do not assume users will count their carbohydrates correctly
  • Switch back to bolus snooze when downward pressure of insulin activity outweighs upward pressure of carb absorption

#Algorithm description:

Using Bolus Wizard or other user-entered carb data, determine whether any uncovered (not yet bolused-for) carbs are present. Calculate as (total carbs entered over last DIA hours / IC ratio) - (boluses over last DIA hours) - (current net basal IOB).

For example, 40g / (10g/U) - 2U boluses - 0.5U basal IOB = 1.5U worth of uncovered carbs.

Determine whether upward pressure of carb absorption currently outweighs downward pressure of insulin activity. Check whether both 5m and 15m delta (i.e. whichever is lower) is greater than 0 and basal BGI (i.e. whichever is higher). So: min(delta, avg_delta) > max(0, basal_bgi) Basal BGI is calculated as current insulin activity (of temp basals only, excluding boluses) times insulin sensitivity. This term will only come into play if basal IOB is highly negative, in which case BG will need to be rising more than expected solely based on the negative insulin activity alone.

If uncovered carbs are present, and BG is rising fast enough to indicate carb absorption is likely occurring, then calculate eventualBG excluding bolus IOB. In other words, allow the loop to determine whether to high-temp or low-temp solely based on current BG and basal IOB, as if the meal and meal bolus had not occurred. If a high-temp is warranted, make sure that the total additional insulin to be delivered over 30m does not exceed the amount required to cover the remaining uncovered carbs.

So for example, if BG is 140 and rising, basal IOB is zero, and the user has bolused 3U of their 40g meal (w/ 1:10 IC ratio, 1:40 ISF, 120 target, and 1U/hr basal), then the loop would be free to high-temp enough to bring the 140 down to target, which would be 0.5U over 30m, or 2U/hr. If BG is 160 or higher, the loop would not give more than 1U of additional insulin, or 3U/hr for 30m.

##A detailed small-meal scenario:

BG 80, -0.5U basal IOB, 20g carbs, 1U bolus, BG rising slightly faster than basal BGI. EventualBG = 100 (counting -0.5U basal IOB, not counting bolus), which is < 120, but since avg_delta > BGI, no action would be required.

After 40m, BG 120 (about half of the carbs have absorbed, and only a little IOB has decayed), and rising quickly. EventualBG = ~140 (-0.4U basal IOB plus positive deviation), so high-temp of 0.5U (to 2U/hr for 30m) recommended.

After 60m, BG 150 (almost all of the carbs have absorbed, and some IOB has decayed), and still rising slightly faster than basal BGI. Basal IOB is now zero after 20m at 2U/hr, so eventualBG = 150, and 0.75U insulin is required, so the high-temp is raised to 2.5U/hr for 30m.

After 75m, BG peaks at 160 and starts to tick downward. The remaining 15m @ 2.5U/hr temp is canceled if the loop is still in range, and it goes back to calculating eventualBG including the remaining ~0.5U bolus IOB, plus the ~0.4U net basal IOB giving an eventualBG of ~124. If the loop is not in range, the full 0.75U is administered over 30m, resulting in an eventualBG of ~110.

##A more general big meal scenario:

BG 120, zero IOB, 120g Thanksgiving dinner with a 6U bolus. As BG starts to rise, the loop high-temps accordingly: 2U/hr @ 140 (up to 0.5U basal IOB), 3U/hr @ 160 (up to 1U basal IOB), and up to 2U basal IOB if the PWD rises to 180. But let's instead say that 1U basal IOB is delivered, and BG starts to drop quickly from 160 due to the 7U of total IOB. High-temp is canceled on the first downtick (or expires), and the loop goes back to bolus snooze. If BG continues to drop as snoozeBG drops below target, the loop will low-temp to ward off a post-prandial low. Once BG starts to rise again, still-uncovered carbs mean bolus IOB can be ignored again, and the loop can start to high-temp as soon as BG starts rising above target. At that point is likely to continue to do so, limited by how high BG rises and how much basal IOB would be required to correct back down to target. (140mg/dL=0.5U, 160=1U, 180=2U). Over the 4h required to digest Thanksgiving dinner, the loop would be likely to administer an extra 2-3U of insulin to assist with meal bolusing. If the PWD did not administer the remaining 3-4U manually, BG would likely rise above 180, and more than 3U total would be given during digestion, and/or BG would remain high after bolus IOB decayed, and continued corrective high-temps would need to be given until BG started to return to normal.

#Conclusion: This algorithm would do a better job than the current oref0 OpenAPS bolus snooze algorithm at assisting with post-prandial rises. Because of the combined requirements for user-entered uncovered carbs, and BG rise showing an ongoing upward carb pressure, this algorithm should be able to do so more safely and reliably than solely relying on the alternatives (up-front bolusing for the entire meal, extended / square wave boluses, or subsequent additional manual boluses). However, in order to be consistent with the OpenAPS design principles, whereby it never administers a temp basal too large to safely run to completion, and is designed to be unable to administer insulin faster than carbs can absorb, this algorithm cannot handle meal boluses unaided. The PWD must still execute a reasonable meal bolus covering most of the meal carbs, and should be prepared to administer additional post-meal boluses if needed.

#Testing: This algorithm, while loosely based on our experience with similar dosing algorithms used in #DIYPS, has a number of new and novel features, and requires extensive unit and real-world testing before being trusted to assist with real-world meal situations. The first step in this process will be identifying scenarios to add to unit tests, so we can verify that the algorithm, as implemented, does the correct thing in all the scenarios we identify. As such, if would be helpful if anyone reading this could think through and write up a few scenarios of how you think the algorithm should behave. These can simply be written up in English, as I've done above, or can be added to the tests I created for the scenarios described above: https://github.com/openaps/oref0/blob/meal-assist/tests/determine-basal.test.js#L246-L331 . Feel free to fork, edit, and PR that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment