Under the hood of OpenAPS

(Pull up a chair, this is a bit of an involved post)

UAM is “unannounced meals” feature in oref1 for OpenAPS.  You can read about UAM features and its development in Dana’s blog post here.

Here’s how my thought process has gone on UAM since I first read about it:

1st thought: Oh, I won’t need that since we always enter our carbs and prebolus.  UAM won’t offer me any improvements since we always “announce” our meals.

2nd thought: Well, there are times (ahem, school snack bar) where Anna or I have some hard carb counts.  I wonder if maybe when we were 20% off on a carb count, perhaps UAM might help then?  Because if we are 20% off on a carb count, that would sort of be like having 20% of an unannounced meal, right?  Hmmmm…let’s try enabling it and see.

3rd thought: Wow, that worked really well.  But WHY did it work well?  How is it different?  Oh piss…I’d better roll up my sleeves and look at code.

And that is how I ended up learning more about oref0’s logic for setting temp basal and (now with oref1) setting SMBs.

Side Note: If you care to do the same as I’ve done…the logic for how oref0 sets temp basal and gives SMBs is laid out in a file called determine-basal.js in GitHub.  This same file is pulled into your rig when you build your loop.  I’m not a code person, but a slow and methodical read of the file can provide some insight into the logic for how temp basals are set.  Plus, there’s certain lines in that file that start with “//”…those are plain english notes to help readers understand the code that follows the note.  If you start with the // lines, that may help anchor some of your read of the file.

When I went to the first endo appointment after diagnosis, I was shocked that they didn’t have a flow chart to tell us how to dose insulin.  Like those decisions flow charts that say “if answer is yes, go to this next square…if the answer is no, go to the other square.”  As I came to learn the hard way, the answer to “How much should I dose for a BG of 250 mg/dl?” will have about 18 questions that follow it before you can give an answer.  What’s her ISF?  What’s her IOB?  Is she exercising?  Is her BG rising or falling?  Did she eat recently?  How long ago did she eat?  How much did she eat?  How confident are you in the carb count?  Is she stressed/excited/nervous?   And so on.  It’s no wonder finding a babysitter for a T1D kid is almost impossible.

The same complications exist for loop logic.  Depending on whether BGs are rising/falling/steady, how fast BGs are rising, whether food is at play, and whether temp targets…how does your loop take those into account?   I’d previously heard OpenAPS developer Scott Leibrand refer to “blending” things like this into his loop, but I didn’t quite understand the term until I really looked into the determine-basal.js code.

So how does that blending work?  Traditionally, you’d start loop math using just insulin and carbs to predict future BGs.  You’d approximate how the insulin would behave based on published insulin effectiveness curves (like peaking insulin activity around 60-90 minutes and then tailing off) and you’d approximate carb absorption (like the Loop system does using the model from Think Like A Panceas).  You’d add the downward effect of insulin and the upward effect of carbs and voila…a predicted BG curve blending insulin and carb effects.

But, a good loop will go beyond that simple approximation to try to help with things that are happening outside the perfect-world scenario.  Things like “retrospective correction” and “BG momentum” in Loop system attempt to incorporate the “wtf is happening now? I’m not sure.” part of diabetes into a better BG prediction.

OpenAPS does this refinement through the use of several different predicted BG curves.  Ever wonder WHY OpenAPS has all those purple lines showing in Nightscout?  This is the reason why…those lines are the drivers of the logic.  They help blend the various scenarios about rising/falling/steady BGs, recent food, and activity to come up with a better chosen predicted BG curve.  And a better predicted BG curve should yield a better loop decision…avoiding lows and quicker recovery from high BGs.

Ready for this?  OpenAPS has the following types of “basic” predicted BG curves:

  • IOBpredBGs: based on insulin only
  • COBpredBGs: carbs and insulin together
  • UAMpredBGs: “floating carbs” and insulin together

That’s not the exhaustive list of curves used though…depending on the situation (rising BGs or falling BGs, for example), OpenAPS also does other calculations using the predictedBG curves (for example, minPredBG and avePredBG) to help provide for safe looping.

Basically, determine-basal.js has a whole bunch of “if this (is happening), then do that (use THAT particular predBG curve to determine the delivery of insulin)”.  For example, IF carbs are on-board and UAM and SMB are enabled, and BG is rising but not as much as expected, and IOB is negative, and prediction is above target…THEN use the XXXpredBG curve to calculate how much insulin to deliver.  If you change any one of those IF inputs…like BG is instead dropping and faster than expected…then OpenAPS is going to make a different decision about how to adjust insulin delivery and may use a different predBG curve to do that.

So, determine-basal.js is that complicated set of 18 questions involving “IF this and this and this and this but not that or that, THEN that” that we all used to go through before giving a dose of insulin.  It even has an IF-term called “Deviation” which is basically a measure of how far off expected BG behavior you are.  Looking at the deviation term is a big part of the OpenAPS decision IF-logic.  We’ve all used it before…”dang it, she SHOULD be dropping right now, but she isn’t!”

That’s where UAM and floating carbs come into play in a new way.  Previously, looping was constrained mathematically around meal-times by the amount of carbs entered.  The COBpredBG curve could only go so far in its prediction of BGs because it was limited to the carbs entered.

UAMpredBG is not restricted to the carb entry you’ve provided.  In a way, it doesn’t “trust” your carb entry as much.  It fact-checks your carb entry by looking at deviation comparisons.   More mathematically stated, UAM is predicting the future BGs based on the slope (rate of change in BG) of your actual BGs during meals.  [“during meals” is user-defined as either having carbs on board, an eating soon temp target, and/or a bolus given].  So, if your BGs are rising during a meal and they continue to rise beyond what was predicted on carbs alone (strong deviations happening), UAM is going to carry that prediction forward…regardless of the carbs you entered.  And it will carry that forward until the slope stops and things settle down.  Without UAM, that continued BG prediction up would’ve been restricted shortly after the meal was eaten by the fact that the carbs would’ve decayed ( COB=0 ) fairly quickly based on the rise in BG.

So, do you ever find that perhaps you under-counted carbs?  You’ve watched a meal’s BG climb after unknown sauces and unidentified ingredients were clearly playing a part in the meal?  The recovery to target BG in that situation can be slowed by the fact that the carbs were eventually all “used up” by the loop logic to explain the rise in BG.  Any additional rise the loop had no explanation for, and tended to treat delicately (slowly).

With UAM, the curve says “I can see these deviations were not expected.  I will help.  Even if the deviations go beyond simple explanation of what COB would expect.”  So that mathematical freedom helps for poor carb counting.  (It’s not unlimited freedom, there are still safety mechanisms in place.)

Case in point…that mini McMuffin of unknown carb count.


See how the UAMpredBG is headed to 294, while the other predictions have lower eventual BG predictions?  UAM is taking into account that Deviation of 67 and COB of 16g…saying “wow, you have NOT shown the BG behavior that I would’ve expected at this point in time for the carbs and bolus you’ve told me about.  I’m gonna trust your carb entry less and instead trust the deviations more.  If my predictions are right, you will need 1.85 additional units of insulin to control this unexpected deviation.  But, predictions can change.  So let’s give you a SMB of 0.4 units to help with that right now.  If I’ve overshot my estimate, I can suspend enough basal later to fix. But that 0.4 is a good help right now for your deviations I’m seeing.  Let’s try that, we will regroup with the next BG and see where you are.”

The next BG reading comes in and loop has some new information to consider.  BG has slowed down and there’s more insulin on board from the 0.4 SMB.

Screen Shot 2017-05-22 at 11.16.08 AM

Deviation is now a smaller number (the slope of the rising BGs has decreased), we are moving closer to what the expected behavior should be.  In fact, the UAMpredBG curve now predicts an eventual BG of 104 and the insulin required is now -0.12 units.  So, loop will suspend basals and wait for the next BG to come in…and go through all of its IFs and THENs again before deciding what to do with insulin.  The loop is beginning to be able to trust cob again; it has delivered enough insulin to cover the carbs that we undercounted and, through SMBs help too, we are going to spend less time out of range.

What about situations where you OVER counted carbs?  Will UAM be able to help there too?  Yes.  When UAM is not seeing enough carb absorption compared to what you’ve told it to expect (“hey, you promised me 25g carbs and I’m only seeing enough BG impact to trust that you ate 10g of carbs”), then the loop will hold off on basal insulin and dosing until the BGs catch up.  (See…it’s comparing those COBpredBG curves with the UAMpredBG curves to make decisions.  All those little purple lines and deviation values are part of the logic sequence to deliver insulin.)

Because UAMpredBG curve is not limited so much by your actual carb entry, it will tend to have a much more variable prediction after each new BG entry.  It will predict very high eventual BG if your BG is strongly rising, and then it will predict lower BG if your BG is dropping.  So, if and when, you decide to enable UAM…don’t be surprised by seeing an ever-changing UAM curve, that’s an expected result since it isn’t quite so restricted by your carb entry.

As I understand it, it took several iterations for Scott and Dana to figure out the proper blending (IFs and THENs) of UAM into the oref0 determine-basal.js code.  Then there’s been many early users that have put it to the test to validate or improve the blending.

Where I’d previously thought that UAM had very little value, because we always enter our meals and prebolus…turns out I was wrong.  I’m not a carb-counting expert.  I get it wrong sometimes.  And UAM helps mop-up my harder carb counts, in both directions.  When I’m right on carb counts, UAM plays almost no role…but with UAM I’m not going to be nearly as BG-impacted when I do get things wrong.



2 thoughts on “Under the hood of OpenAPS”

Leave a Reply

Your email address will not be published.