Skip to content

Multi Meal Extended Blueprint

The multi-meal extended blueprint pairs MultiMealExtendedT1DModel with MultiMealExtendedT1DData. It augments the multi-meal model with second-occurrence meal labels so twinning can reach into a second day.

Why extended?

When twinning, it is often useful to run the procedure on more data than strictly necessary. This avoids wrong parameters for the last meals of the window — the "tail effect".

For example: a dinner at 22:00 in a window that ends at 23:00 gives the twinner only one hour to understand the dinner's absorption rate, which is hard. If you have data after the window, you can leverage those extra points to constrain the estimate.

Extended data

The extended blueprint handles this by adding a second occurrence of the breakfast, lunch and snack meals — labelled B2, L2, S2 — each with its own absorption rate (kabs_B2, kabs_L2, kabs_S2) and delay (beta_B2, beta_L2, beta_S2), plus a second breakfast insulin sensitivity SI_B2.

Where to cut the extra data

Since the insulin-sensitivity profile repeats from 04:00, it is advised that the primary window stop at 04:00. Only breakfast has a second-day sensitivity: from 11:00 of the next day the model goes back to the shared SI_L (then SI_D). So, also to keep computation reasonable, the extra portion should not extend much past 11:00 of the next day.

Usage

from py_replay_bg.data.multi_meal_extended_t1d_data import MultiMealExtendedT1DData
from py_replay_bg.model.multi_meal_extended_t1d import MultiMealExtendedT1DModel

rbg_data = MultiMealExtendedT1DData(data=df, environment=rbg.environment)
model = MultiMealExtendedT1DModel(u2ss=rbg_data.u2ss, tsteps=rbg_data.tsteps,
                                  t_start=rbg_data.t_start)

Here t_start matters at every step. As in multi-meal, the hour from the t_hour channel picks SI_B (04:00–10:59), SI_L (11:00–16:59) or SI_D (otherwise); in the breakfast window the model uses SI_B2 instead of SI_B once t + t_start >= 1440, i.e. from midnight of the day after the one the data starts in. Second-day lunches and snacks are absorbed through kabs_L2/kabs_S2 but still use SI_L/SI_D.

Its input channels (data_to_input) extend the multi-meal ones with the second-occurrence meals: meal_B, meal_L, meal_D, meal_S, meal_H, meal_B2, meal_L2, meal_S2, bolus, basal, t_hour, forcing_ip, forcing_ra.

Preparing the data

Flag the meal events of the additional portion by appending a 2 to their labels: B → B2, L → L2, S → S2. There is no need to relabel the insulin boluses, nor the hypo-treatments. Dinners have no second-occurrence label, and any other label is silently dropped. For example:

import numpy as np

# Split at 04:00: everything after is the "extra" second-day portion.
hours = np.array([t.hour for t in df.t])
idx_split = np.where(hours == 3)[0][-1] + 1

for label in ('B', 'L', 'S'):
    idx = np.where(df.cho_label.values == label)[0]
    idx = idx[idx >= idx_split]
    df.loc[idx, "cho_label"] = label + '2'

Parameter vector

Relative to multi-meal, the extended blueprint adds the second-occurrence parameters:

kabs_B2, kabs_L2, kabs_S2, beta_B2, beta_L2, beta_S2, and SI_B2.

Example

A complete extended twinning example lives in example/twin_multi_meal_extended.py. Its unknown_parameters_prior estimates the multi-meal parameters below (it does not estimate f, ka2 or kd) plus the extra second-day entries:

unknown_parameters_prior = {
    # ... the multi-meal parameters (Gb, SG, p2, kempt, SI_B/L/D,
    #     kabs_B/L/D/S, beta_B/L/D/S) ...
    'kabs_B2': {'prior': LogNormal(mu=-5.4591, sigma=1.4396), 'min': 0, 'max': .5},
    'kabs_L2': {'prior': LogNormal(mu=-5.4591, sigma=1.4396), 'min': 0, 'max': .5},
    'kabs_S2': {'prior': LogNormal(mu=-5.4591, sigma=1.4396), 'min': 0, 'max': .5},
    'beta_B2': {'prior': Uniform(a=0, b=60), 'min': 0, 'max': 60, 'integer': True},
    'beta_L2': {'prior': Uniform(a=0, b=60), 'min': 0, 'max': 60, 'integer': True},
    'beta_S2': {'prior': Uniform(a=0, b=60), 'min': 0, 'max': 60, 'integer': True},
    'SI_B2':   {'prior': Gamma(alpha=3.3, beta=1 / 5e-4), 'min': 0, 'max': .1},
}

Replaying only the primary window

The extra portion exists only to constrain the twinning fit. example/replay_multi_meal_extended.py replays the saved rbg_data, i.e. the full twinned window. To replay only the primary window, build the data object from the trimmed frame (df.iloc[0:idx_split, :]) and the model with that object's tsteps and t_start.