LoopKit / Loop

An automated insulin delivery app for iOS, built on LoopKit
https://loopdocs.org
Other
1.48k stars 1.29k forks source link

Integral retrospective correction #695

Closed dm61 closed 5 years ago

dm61 commented 6 years ago

Integral Retrospective Correction (IRC) (updated July 16, 2023):

Some important points and an algorithm description are as follows:

  1. Known risk factors compared to standard Loop: With IRC turned on, in response to persistent discrepancies between observed and predicted glucose motion, Loop will likely increase insulin corrections, which may increase the risks of hypoglycemia. IRC may also lead to increased oscillations ("roller-coaster") in glucose responses. Both of these risk factors are higher if the user's setting value for Insulin Sensitivity (ISF) is too low. Increasing ISF setting value tends to mitigate these risks but it is impossible to offer any guarantees for anything around T1D.

  2. Compared to standard RC, IRC is more likely to improve glucose control in the following scenarios:

  1. In some scenarios IRC does not differ from standard Loop RC

    • Regardless of the current glucose level, neither RC nor IRC is adding to the glucose forecast during the times when the absorption rate of announced carbs is greater than the minimum absorption rate.
    • Neither RC nor IRC effects depend on glucose level; both depend on discrepancies between predicted and actual glucose responses.
  2. Please do not expect immediate or very substantial improvements in blood glucose control. A one-time success after turning IRC on does not really mean that IRC "works" - this could just as well be a temporal coincidence. Some ways to decide if IRC could be safe and effective for you include:

    • Responses to unannounced meals - spikes should in general be somewhat lower than with standard Loop, but there should also be no follow-up lows
    • Nighttime responses over a few weeks - highs or lows should be less frequent compared to standard Loop; at the wake-up time blood glucose should in general be closer to the correction range.

**** IRC Algorithm Description **** Integral retrospective correction (integral RC) is an experimental modification of the Loop's retrospective correction (RC) algorithm. Just like the standard RC, the motivation behind IRC is to make Loop less dependent on how well carb entries, ISF, CR, or programmed basal rates represent reality, and to improve responses in the presence of any unmodeled factors.

Operation of the standard Loop retrospective correction is illustrated below, where time = 0 represents the present time.
rc_illustration A modeling error called discrepancy is calculated as the difference between the actual BG and the BG predicted based on insulin and carbs models over the past 30 minutes. In the example shown, discrepancy = 15 mg/dL. The discrepancy is then used to modify the BG forecast over a correction interval of 60 min. The net effect of standard RC is that the eventual predicted BG is adjusted up (or down) by a retrospective correction equal to the current discrepancy (subject to momentum effects, but that’s a different topic).

In the integral RC, the correction magnitude and the correction time interval depend not only on the current discrepancy but also on past discrepancy values, so that the correction becomes more aggressive if the discrepancy persists over consecutive Loop cycles. This results in more aggressive dosing in response to any persistent modeling errors due to over/under-estimated carbs, parameters (ISF, CR, or basal rates) being away from reality, exercise, … whatever.

Simulation examples

In the first example, the actual basal needs increase by 40% (to 0.7 U/h) over a period of about 10 hours. Standard Loop high temps, which keeps BG from ramping up, but BG hovers away from the target. Integral RC kicks in more aggressive high temp corrections, which brings BG closer to the target.

22g_12h

In the second simulation example, the actual basal needs fall by 40% (to 0.3 U/h) over a period of around 10 hours. Standard Loop responds by low temping, but BG still drops below suspend threshold, which triggers some on/off oscillations around the suspend threshold. Integral RC results in a smaller BG drop and keeps BG closer to the target throughout.

-22g_12h

The third example is a case of unannounced 25 g carbs with an absorption time of about 3 hours. Compared to the standard Loop, integral RC reduces the magnitude and the duration of the spike.

25g_3 5h

Integral RC math

Integral RC math is relatively simple. Overall retrospective correction is computed as a sum of two components, proportional and integral:

overallDiscrepancy = proportionalGain * discrepancy + integralDiscrepancy

where integralDiscrepancy is computed as:

integralDiscrepancy = integralForget * integralDiscrepancy + integralGain * discrepancy

proportionalGain, integralForget and integralGain are constant parameters of the integral RC filter. In the standard RC, proportionalGain = 1, and integralGain = 0. In simple control theory terms, standard RC can be viewed as a proportional (P) controller trying to reduce modeling error (discrepancy). Integral RC adds an integral (I) action to this control loop. In addition to the above basic math, safety provisions (integration resets and limits) are included to minimize the chances of over-correction. A differential term has been added to mitigate the sluggishness of the response when discrepancies change polarity.

The IRC code can be viewed here

ChrisHanzic commented 6 years ago

Switched over from OpenAPS to give this a go. I love the interface of loop. I previously found Loop to not be aggressive enough - So I have high hopes for this modification to the algorithm. Would be happy to provide some feedback over the coming weeks. Thanks @Kdisimone for your IRC-spike-beta repo!

chadharper0131 commented 6 years ago

Im starting to think... (yes, i know its rare! like seeing a unicorn rare!) ---------->Maybe, just maybe, for each & every { "feature", "option", "setting", "function", "parameter", "range", "target", "goal", or other misc. changes ~ etc...} I think we should add either a corresponding [on|off] style button under the [configuration] menu, or a sub-config menu named: ?? "[beta features]" or something similar. This sub-menu, can be accessed/locked/unlocked using the normal fingerprint sensor, or a separate PIN(4 digit), for adults to have more control but still allow children to have access, and issue bolus commands. All Buttons should be inextricably tied to the features, along with detailed "throw .error[1-5] list(s)..." , that are auto included into Issue Reports. Errors, along with detailed logs, containing all the timing + usage information about when "feature x, y, or z" was turned [on]or[off] at "3:00pm - 3:31pm, for +31 minutes". (for both individual use &/or group evaluation & future data-set purposes). So---> the standard Package -would be (1)Feature, (2)Button, (3)Error & (4)Info logs, (5)beta feature(s) lock ability, and (6)readme addition - addition to a "features_readme.txt" file describing the the changes to the code, code files & location, description of feature and any related data (pics, potential +'s or -'s, contraindications, warnings, avg results seen, + user comments., etc...)

My feeling is (A.) having a standard procedure for adding new "stuff", would simplify things all around, and (B.) it doesn't limit testing of new/experimental features only to the ppl who are able to physically code them in... although that isn't a horrible way to stop people from trying out potentially dangerous stuff that they don't necessarily understand fully. The requirement to actually learn and implement changes into the code does inherently force ppl to understand & think about the changes they are making at a more than superficial level, but in many instances, it is parents building Loop for their kids, & the kids aren't necessarily old enough to comprehend the changes, or the potential implications of changes made to the code, so this point become null.

I have to say though, Medtronic doesnt "allow" us access, so we are stuck using older, very specific models of pumps, and we dont really appreciate that much, so in the spirit of openness - should we not try to give as many ppl as much access and choice as possible? In my experience, i have been pleasantly suprised by the level of awareness, conscious decision making, and extremely high ethical standards the DIY, Open Source, "artificial pancreas", -makers have consistently held themselves to!!(somehow without anyone protecting us from ourselves ::cough:: "fill-in-the-blank-of-your-fav-regulatory-body" ::cough::, ::choke!!:: ).

So does this represent an unnecessary &/or ineffective burden being placed on users? & is there a more effective way to encourage deeper level of understanding, and assist users in the process of acquiring a more fundamental knowledge of the Loop system(s) in the general sense (i.e- How general diabetic & pumping principles translate to the closed-looping world, and how loop specifically has gone about implementing these principles/ideas into the code base.)???

**(Over the years, ive participated in, and helped run (2x participant, 2x volunteer) these free 10-day Vipassana meditation retreats - run by volunteers. They have this CraAzyY! way of running things: give the person who absolutely does NOT want any power or authority, the most!! ...and, as far as i know, and from my experiences there, it works pretty darn well. If interested, check em out @ dhamma.org! - i think there is something to be learned from this idea, but not sure 'if', 'how', or 'what' exactly. ~~~> Anyone?!)

==================================================================================

+Dana & Scott have done an excellent job, and have obviously spent a very large chunk of time, to provide this challenging type of support and documentation about the "oref0" code, & the OpenAPS system in general. For users who may be new to programming, or who are trying to deepen their Loop specific code/system(s) knowledge -->For Loop: @Kdisimone has been extremely generous with her time & skill & gone through all the old Loop documentation & clarified, cleaned-up, organized, updated, and infused new life into it, and it serves (at least for me) as the "Go-TO:" resource for Loop users. Especially for new users who are implementing their first few "loops", and for users who have yet to become acquainted with Loops nuances & idiosyncrasies. For the Loop community as a whole; in regards to code changes, new features, bug fixes, etc..., My thinking is such- the larger the user base & test groups, the more bugs and errors can/will be found and fixed(hopefully before being included into a master release), & the safer Loop will ultimately be. My safety concerns about loop, tend to lean towards the idea that - if there is a serious safety issue/concern, it will likely be an isolated incident(s), where loop reacts unexpectedly to some kind of unanticipated event.(first thought- when Dexcom G6 comes out... its initial integration into Loop) after 1000's of hours looping, I believe the backbone of the Loopkit/Loop code is very safe and effective. That belief is founded primarily upon the entirety of my experiences using Loop day-in & day-out, not on my knowledge of the code or its specific implementation.

Thats where OpenAPS, OrefO, seemingly has an advantage(whether real or perceived). They have put some serious time and energy into the code to ensure - no1 slips through any cracks, period. And any changes, features, etc..., come with an explicit warning about any potential changes in risk and/or safety! ---> Maybe time to make this its own thread?! ...or not. Thoughts?!

dm61 commented 6 years ago

If you are testing integral RC with Loop 1.5.4dev or 1.5.5dev, or later, please make sure you are using the most up-to-date integral-retrospective-correction branch, which includes a fix for an issue that apparently surfaced with Loop 1.5.4. If you are testing integral RC with an older Loop, everything should be fine.

Details are as follows: for reasons that are not clear to me, Loop 1.5.4 (or later) may launch retrospective correction calculations multiple times per Loop cycle. With standard RC, this behavior seems to be superfluous, but most likely does not affect Loop predictions or dosing. With integral RC, however, this may result in unintended updates to integral action, and thereby larger than intended changes in prediction (and dosing). The fix in the updated integral-retrospective-correction ensures that integral action is updated only after a new glucose value is received, i.e. only once per Loop cycle,

lgruen commented 6 years ago

Thanks so much for pointing this out! Will recompile with your fix right away when I get home. FWIW I hadn't noticed any issues so far with 1.5.4 and integral RC.

trixing commented 6 years ago

Thanks for the fix. I observed an interesting night without this fix - not sure if that's related, but I'm wondering if IRC is working here as expected. Unfortunately I didn't get any logs from the device (we should really write them to a file). See picture below. The jagged line is IOB. Apart from the sensor being noisy, I would have expected IRC to increase IOB over time to counter act the fact that BG isn't going down? For the curious this is a night after a very protein heavy meal.

image

dm61 commented 6 years ago

@trixing not a good night, sorry about that. I would also expect that IRC should have resulted in more aggressive corrections in that high-protein case. I do not think this had much to do with the fix. More likely it's sensor noise. IRC action is reset when discrepancy changes polarity. This is an important algorithm feature, to minimize delays in action upon bg trend reversal. Unfortunately, high sensor noise may trigger such resets often, thus diminishing IRC effectiveness. I do not know of a good way to address this - it is very difficult to distinguish between noise and true bg variations.

scottleibrand commented 6 years ago

If you use a 15m average for calculating your discrepancies, it is a lot less likely to reverse polarity solely due to noise, but will still reverse within 10m or so of a real reversal.

dm61 commented 6 years ago

Hi @scottleibrand, thanks. Loop's "discrepancy" is actually a 30m average, while "momentum" is calculated over 15m, but that bg trace had some pretty large jumps.

trixing commented 6 years ago

To be fair, treating this as a human is also complicated without overshooting. While the high was unfortunate I'd consider it a success that it didn't end up in a low needing treatment with fast acting glucose.

From looking at the graph though I'd guess some smoothing and retroactive calculation over longer time period should make it clear to the algorithm that the insulin isn't having the desired effect.

Would you be interested in a csv of the raw data to play with the algorithm?

dm61 commented 6 years ago

@trixing I agree, we should be able to do better smoothing of bg data compared to simple averaging - that's one of the areas I'd like to tackle in the future. Sure, would be good to have your data to test out approaches - thanks.

dm61 commented 6 years ago

I've added an indicator to the integral-retrospective-correction branch. In the Retrospective Correction line of the Predicted Glucose screen, RC value shows the impact of RC (or integral RC) on bg forecast. An up (or down) arrow is present if integral RC is active and increasing (or decreasing) bg forecast beyond what standard RC would do.

rc_indicator

trixing commented 6 years ago

Here is an export of the Nightscout data in csv format:

high_night_irc_20180512.txt

dabear commented 6 years ago

What would it take to make this feature mergable to loop master, @dm61 @ps2 ?

dm61 commented 6 years ago

@dabear #726

raulmvm commented 6 years ago

Hi @Kdisimone have you got your branch "IRC-beta" fixed with this issue? Thanks in advance and thanks very much for the fix @dm61, so interesting...btw I use Katie´s IRC-beta branch with no issues, but I also changed "carbEffectlimit" from 30 to 15, where I just changed that value in Xcode, right? or do I need anything else for the change? TIA

dm61 commented 6 years ago

@raulmvm as far as I can tell Katie's IRC-beta branch is based on an earlier version of Loop, so no need to apply the most recent IRC fix, And yes, if you wanted to change "carbEffectlimit" from 30 to 15, you just change that value in the code and install, no need to do anything else.

birdfly commented 6 years ago

@dm61 any possibilities to add a Derivative (D) factor to make a full PID control algorithm?

aditmer commented 6 years ago

Here are some anecdotal results of using IRC for the past week. As always, n=1, but the IRC algorithm changes have made a huge difference for my daughter. It's as big as an improvement for us as starting to Loop was in the first place (if not more).

Take a look at this daily stats report. You can see the huge improvement of time in range. Sunday, July 28th 2018 is the first day she has had 0% high readings since we've had a CGM. We did have more lows, but they were shallow and short. I simply raised ISF and the target range and these are becoming fewer as well.

screen shot 2018-07-31 at 8 59 03 am
francesc0-cgm commented 6 years ago

I confirm that last release of IRC by @dm61 have not any safety issue using isf/ insulin carb ratio to determine the prediction also on kids. I believe it could be added in dev @ps2 (i am using it since dm61 got it out)

diggabyte commented 6 years ago

@birdfly regarding derivative correction, I asked about this over in gitter as well. Here was the reasoning from @dm61

I've tried adding a derivative term to RC. In theory, this could improve the response some more, but in practice noise in sensed bg values lead to somewhat erratic behavior so I decided it was not worth it.

dm61 commented 6 years ago

@birdfly sorry for the delay - I was away on a trip for an extended period of time and have missed your question. @diggabyte thanks for filling in the answer.

@francesc0-cgm thanks for early testing and for providing feedback, which contributed to carbEffectLimit setting based on ISF and CR, as opposed to a preset fixed value. Glad to hear this is working for you.

@aditmer thanks for the feedback, very happy to see good results.

diggabyte commented 6 years ago

@dm61 just some initial feedback: I'm only 24hrs into running IRC and I've noticed that there is a significant increase in sinusoidal oscillation to my bg levels in comparison to either RC or no-RC. I'm wondering if perhaps IRC could be amplifying inaccuracies in my ISF and/or IC ratios?

Although I'm 6-wks into looping, I felt as though I am pretty accurate with my ratios and basals by now, but perhaps I need to take a closer look with some autotuning / fine-tuning to see if the outcome improves.

I'll collect more data in NS over the next few days and report back.

dm61 commented 6 years ago

@diggabyte what you are observing is not unexpected with ISF value on a low end. IRC does tend to amplify the effect of lower ISF. If you increase your ISF value some more, those oscillations should subside and either go away completely or become smaller in amplitude. Another mechanism that may result in some oscillations typically around the correction range is when basal is a on a high side, and bg forecast then tends to cross through suspend threshold. In such cases, IRC also tends to amplify the resulting oscillations compared to RC or no RC cases. I might be able to provide some more feedback if you post your NS and/or Loop screenshots (bg cart and carb absorption chart).

diggabyte commented 6 years ago

@dm61 thanks, that largely confirms my suspicions. I bumped ISF earlier today with that in mind and it has indeed reduced the amplitude of the oscillations. I’ll analyze basals and suspend more closely and see if I can flatten this out even more. Thanks for the feedback and of course for creating this!

francesc0-cgm commented 6 years ago

3f6234ba-565c-43e0-9a0d-edede330427b

Today i had this strange prediction using last dev woth irc. My son was going high with negative iob and a bit of carbs on.

ps2 commented 6 years ago

@francesc0-cgm Please use a separate ticket, unless you have some reason to think it is IRC related. Also, an issue report and Loop screenshots will be more useful than a NS shot; the IOB and COB pills are not particularly reliable in NS with Loop, and so we recommend people turn those off. This screenshot doesn't give us enough to go on, unfortunately. Hovering over the Loop pill might give more info.

francesc0-cgm commented 6 years ago

So don’t mind about it...

deddynh commented 6 years ago

IRC=Fantastic. I have been a long OpenAPS user and like UAM and SMB as my carb counts are close but never exact. I have not had as much success keeping the rise lower than 160 mg/dl post prandial until IRC using Loop. So fantastic. THANK YOU @dm61 for putting this out. Would love to see this in dev if people are having the same experience using it. I can't wait for this to all come to Open Omni!

dm61 commented 6 years ago

@davidkeddydb great, thanks for the feedback! I should say IRC is just a small piece on top of Loop foundations, glad to hear it's altogether working well for you.

christerjensen commented 6 years ago

IRC is working great most of the time, but I find it is to conservative when the BG is low and falling slightly. This example is from lunch (girl 8 yrs), only 0.09 IOB, the prediction in EvBG is 0,9. Carbratio is 1:12, so 40 g should give roughly 3.0 units (slightly less with BGTarget around 6.0 mmol/L). The suggested insulin is only 1.5 U. Loop has been working fine with these ratios for nearly 2 years. Do people experience the same? Any suggestions. I could change carbEffectLimit, but then it would also be less aggressive when BG is high. I think the Loop version is 1.5.5dev (from 30.06.2018) irc

dm61 commented 6 years ago

@christerjensen Thanks for posting this question, which points to a broader issue of how Loop doses insulin when bg is below Correction Range and close to or below Suspend Threshold. What happens in your example is as follows: Loop has been zero temping for some time to counteract BG trending down faster than expected. The zero temping was reinforced by IRC integral action, which resulted in even lower predicted BG (this is in most cases desirable, as it better helps prevent lows. However, because of this amplified prediction in the negative direction, bolus recommendation can then be more significantly curtailed when BG is close to Suspend Threshold (you may take a look at #533 to see why that is the case). Once BG starts ramping up, BG prediction quickly swings in the positive direction, which is fine. However, a part of the current Loop temp-basal algorithm is to not high temp if any predicted BG is below Correction Range. That's another reason you see a longer delay in high temping, and a higher spike. To summarize, what you are observing is a combination of (1) bolus recommendation curtailing, amplified by IRC's prediction in the negative direction, and (2) a lag in Loop high temping due to no-high-temping below Correction Range provision. We could try to address one or both of these in various ways, and I'll be looking into that. In the meanwhile, you may consider manually overriding bolus recommendation in situations such as the one you posted.

apabari commented 6 years ago

@dm61 - This functionality seems to be working well. Thank you!

What will it take for this to be "ready" to be merged into the dev branch officially, or perhaps into the next production version of Loop?

dm61 commented 6 years ago

@apabari thanks for the comment, glad to hear IRC is working well for you. We want to make some changes in how the IRC code is implemented, to make it fully consistent with the rest of Loop. This will also open some other interesting opportunities for further improvements. In the meanwhile, the existing version of IRC is fine for testing purposes.

Trpl7ca commented 1 year ago

By increasing ISF you mean a smaller number, correct? I have had the roller coaster going

dm61 commented 1 year ago

By increasing ISF you mean a smaller number, correct? I have had the roller coaster going

By increasing ISF setting value I mean having a larger number entered.