Understanding Glucose Forecast in AndroidAPS Part 2

What is the article about?

Let's try to develop our thoughts on glucose forecasting in AndroidAPS. Earlier in the article, I described what forecasts exist and outlined how decisions are made. Now let’s clarify and improve our understanding of glucose forecast. After all, we are ultimately not interested in the forecast itself, but in how much insulin, based on all the input data, the system will decide to give us in order to regulate glucose in the blood. This is important, since the system has several forecasts, and not one single and correct one, as one might assume. So let's start solving this problem from the end.

We will not analyze the basic AndroidAPS from Milos, but the version from Matthew (MTR93600, link to GitHub), I work directly in this one (link to GitHub) version, which is a modified version of the Matthew application in terms of forecast). The idea is to display the most relevant forecast and introduce separate coefficients for three types of loads (light, medium and heavy), as well as a separate interface for convenient interaction with loads, display the application in a watch. Write in the comments, would such an improvement be relevant to you? In the meantime, I’ll continue on topic…

Disclaimer

By the way! This article is still an attempt to understand the forecasting algorithm in the AndroidAPS program and invites discussion. It should not be taken as the final truth in relation to the forecast. I try to provide code and arguments that you can evaluate on your own, and if you have the opportunity to look at the code, then challenge the presented logic.

How the final forecast is made

Parameter for final calculation of required insulin

So, we have such a parameter as insulinReq – this is how much insulin, based on all previously calculated data, we need to get in order to bring glucose to the goal.

First, let's decide on the logic of which file we will work with. We have a choice of OpenAPSSMBAutoISF/determine-basal.js and OpenAPSSMBDynamicISF/determine-basal.js, I will choose the latter, which implies that the dynamic ISF setting is enabled in the functions.

Here's the calculation insulinReq (Path OpenAPSSMBDynamicISF/determine-basal.js)

var insulinReq = 2 * Math.min(0, (eventualBG - target_bg) / future_sens);
  • minPredBG – minimum predicted glucose level calculated based on various scenarios (eg active insulin, carbohydrates and autonomic carbohydrate management).

  • eventualBG — the final forecast of glucose levels, calculated based on logic, which will be discussed in detail below.

  • target_bg — target glucose level.

  • future_sens — insulin sensitivity in the future (we will probably change this parameter to work with loads).

The system can decide to deliver insulin via micro bolus (SMB) or temporary basal rate

Glucose level forecast: eventualBG and minPredBG

Before calculating the insulin dose, the system makes predictions. Finally, forecasts are built on the basis of the eventualBG parameter with a limitation in the form of minPredBG.

eventualBG itself is calculated in different ways and takes into account depending on various factors naive_eventualBG + deviation; IOBpredBGs, UAMpredBGs, ZTpredGGs, COBpredBGs.

Since we plan to build our forecast that is closest to the final one, I will give the logic for constructing a forecast based on the above parameters:

naive_eventualBG

var eventualBG = naive_eventualBG + deviation;

This method is based on forecasting glucose levels, taking into account deviations based on the current dynamics of changes in glucose levels. However, this method does not take into account more complex scenarios such as active carbohydrates or UAM.

Using forecasts based on IOB, COB and UAM:

Let's consider these forecasts

if (meal_data.carbs) {
    // Если UAM не включён, и есть прогноз на основе COB, выбирается максимальное значение между IOB и COB
    if (!enableUAM && minCOBPredBG < 999) {
        minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG));
    }
    // Если есть прогноз на основе COB и UAM включён
    else if (minCOBPredBG < 999) {
        var blendedMinPredBG = fractionCarbsLeft * minCOBPredBG + (1 - fractionCarbsLeft) * minZTUAMPredBG;
        minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG, blendedMinPredBG));
    }
    // Если только UAM включен, используется прогноз UAM
    else if (enableUAM) {
        minPredBG = minZTUAMPredBG;
    }
    // Если нет данных о COB или UAM, используется Guard прогноз
    else {
        minPredBG = minGuardBG;
    }
}

Analysis of conditions:

  • If carbohydrates were consumed (that is meal_data.carbs matters) and UAM is not enabled (unaccounted meals), then the minimum glucose value is calculated as the maximum value between the IOB-based prediction (minIOBPredBG) and COB-based forecast (minCOBPredBG).

  • If UAM enabled and carbohydrate data (COB) is present, calculated mixed forecast (blendedMinPredBG), which takes into account both active carbohydrates and UAM predictions.

    blendedMinPredBG

    var blendedMinPredBG = fractionCarbsLeft * minCOBPredBG + (1 - fractionCarbsLeft) * minZTUAMPredBG;
  • If only UAM is enabled, but carbohydrate data is missing or not current, the UAM-based forecast is used (minZTUAMPredBG).

  • If neither carbohydrate nor UAM data are available, the forecast is based on Guardwhich is the safety value (minGuardBG), protecting against hypoglycemia.

    • fractionCarbsLeft – this is the proportion of carbohydrates remaining for absorption that have not yet been absorbed by the body. This proportion helps weigh the contribution of each forecast.

    • minCOBPredBG — forecast based on active carbohydrates.

    • minZTUAMPredBG — forecast based on UAM data (accounting of unaccounted meals).

Thus, this blended forecast gives more weight to carbohydrate-based forecasts if there are a lot of carbohydrates left, and to UAM forecasts if carbohydrates are already almost completely digested.

A special case for UAM without carbohydrates:

If carbohydrates have not been registered but UAM is enabled:

else if (enableUAM) {
    minPredBG = round(Math.max(minIOBPredBG, minZTUAMPredBG));
}

In this case, the system simply takes the maximum value between the IOB and UAM forecasts.

Final adjustment minPredBG:

minPredBG = Math.min(minPredBG, avgPredBG);

At the end, the minimum predicted value is adjusted so as not to exceed the average predicted value (avgPredBG) to avoid overly optimistic forecasts.

avgPredBG

This average predicted glucose valuewhich is calculated taking into account all available forecasts. It is used to adjust the minimum predicted value (minPredBG), avoiding too pessimistic or, conversely, too optimistic forecasts.

Here is the code that calculates the average predicted glucose value, avgPredBGbased on various predictions:

var avgPredBG;
if (minUAMPredBG < 999 && minCOBPredBG < 999) {
    // если активны оба прогноза UAM и COB, считаем среднее, взвешенное на основе остаточных углеводов
    avgPredBG = round((1 - fractionCarbsLeft) * UAMpredBG + fractionCarbsLeft * COBpredBG);
} else if (minCOBPredBG < 999) {
    // если нет UAM, но есть прогноз COB, берем среднее между IOB и COB
    avgPredBG = round((IOBpredBG + COBpredBG) / 2);
} else if (minUAMPredBG < 999) {
    // если есть UAM, но нет COB, берем среднее между IOB и UAM
    avgPredBG = round((IOBpredBG + UAMpredBG) / 2);
} else {
    // если есть только IOB-прогноз
    avgPredBG = round(IOBpredBG);
}

Explanation:

  • avgPredBG is the average predicted glucose value based on the available forecasts. It plays a role in the final correction of the minimum predicted value (minPredBG) to avoid making overly harsh or optimistic forecasts.

  • If available both forecastsUAM And COBthen the system calculates a weighted average between them. The contribution of each prediction is determined based on the residual carbohydrates (fractionCarbsLeft).

  • If only a COB (active carbohydrate) based forecast is available, the system averages the IOB and COB values.

  • If only a UAM-based forecast is available, the system takes the average between IOB and UAM.

  • If there is neither a COB forecast nor a UAM forecast, only the IOB-based forecast is used.

How naive_eventualBG and IOBpredBGs relate

Forecasts based on naive_eventualBG And IOBpredBGs have different goals and take into account different factors, but can work together depending on the situation. Let's look at how they interact and when they are used:

naive_eventualBG:

  • Description: This is a simple prediction that is calculated based on your current active insulin (IOB) and insulin sensitivity (ISF) levels. It assumes that there will be no significant changes in the future, such as food intake or a significant change in activity level.

  • Calculation formula:

    var naive_eventualBG = round( bg - (iob_data.iob * sens) );

    Here bg is the current glucose level, and iob_data.iob – the amount of active insulin that remains in the system multiplied by insulin sensitivity sens.

  • When to use: It is applied as main forecastwhen the system does not have information about carbohydrate intake (COB) or other factors that affect glucose levels such as activity (UAM). This is the most basic approach to forecasting and its accuracy may be limited unless additional factors are taken into account.

IOBpredBGs:

  • Description: This is a forecast that is based on insulin dynamics, taking into account how insulin will act over time. Forecasts IOBpredBGs are based on multiple future points in time, estimating how insulin will lower blood sugar depending on its activity.

  • Calculation formula:

    var predBGI = round((-iobTick.activity * sens * 5), 2);
    if (!TDD) IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI + predDev;
                else IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD * (Math.log((Math.max( IOBpredBGs[IOBpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ),2)) + predDev;

    Here iobTick.activity is insulin activity at each time point (for example, after 5 minutes) multiplied by insulin sensitivity.

  • When to use: IOBpredBGs always used even if food intake (COB) or active carbohydrate activity (UAM) data are not available. This is a more dynamic forecast, since it is based on changes in insulin action in the future and is more accurate than naive_eventualBG.

How do they relate?

Do they work together?
In some cases, they may work together. naive_eventualBG can be used as a simple baseline forecast when the system has little information (for example, no carbohydrates or UAM function is inactive), and can be taken into account along with other data. However, if more accurate forecasts such as IOBpredBGs are available, the system will prefer them.

There are situations in the code where the naive forecast (naive_eventualBG) is combined with other predictions to give a more accurate result:

For example, the code could use eventualBGwhich includes adjustments based on IOBpredBGs, deviation (deviations), and can also take into account naive_eventualBGif there is no other data:

var eventualBG = naive_eventualBG + deviation;

Interaction example:

  • naive_eventualBG used if the system does not have COB and UAM information.

  • IOBpredBGs always used, regardless of availability of carbohydrate or activity data.

  • If COB and UAM data are available, they can also be taken into account for more accurate forecasting (COBpredBGs and UAMpredBGs).

When is only one of them used?

  • If the system has minimal data and no food intake (COB) or active carbohydrate activity (UAM) information, then the forecast will be based on naive_eventualBG.

  • If complete information about insulin, carbohydrates and other factors is available, the system will use more complex predictions such as IOBpredBGs, COBpredBGs And UAMpredBGswhile the role naive_eventualBG becomes secondary.

    Total naive_eventualBG is a backup forecast that may be useful in the absence of other data, but IOBpredBGs is always used, since this forecast is more accurate and takes into account the temporary effect of insulin. These predictions may work together, but if there is more complex datasuch as COB or UAM, the system will prefer them for more accurate glucose level prediction.

Conclusion

The final glucose forecast is an eventualBG, which is an inference from other forecasts and limited by minPredBG.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *