def test_carb_effect_with_zero_entry(self): input_ice = self.load_ice_input_fixture("ice_35_min_input") carb_ratio_tuple = self.load_schedules() default_absorption_times = self.DEFAULT_ABSORPTION_TIMES carb_entry_starts = [input_ice[0][0]] carb_entry_quantities = [0] carb_entry_absorptions = [120] (absorptions, timelines, entries # pylint: disable=W0612 ) = map_( carb_entry_starts, carb_entry_quantities, carb_entry_absorptions, *input_ice, *carb_ratio_tuple, self.INSULIN_SENSITIVITY_START_DATES, self.INSULIN_SENSITIVITY_END_DATES, self.INSULIN_SENSITIVITY_VALUES, default_absorption_times[0] / default_absorption_times[1], default_absorption_times[1], 0 ) self.assertEqual(len(absorptions), 1) self.assertEqual(absorptions[0][6], 0)
def test_dynamic_glucose_effect_absorption_never_fully_observed(self): input_ice = self.load_ice_input_fixture("ice_slow_absorption") (carb_starts, carb_values, carb_absorptions ) = self.load_carb_entry_fixture() carb_ratio_tuple = self.load_schedules() default_absorption_times = self.DEFAULT_ABSORPTION_TIMES carb_entry_starts = [carb_starts[1]] carb_entry_quantities = [carb_values[1]] carb_entry_absorptions = [carb_absorptions[1]] (expected_dates, expected_values ) = self.load_cob_output_fixture( "dynamic_glucose_effect_never_fully_observed_output" ) (absorptions, timelines, entries, # pylint: disable=W0612 ) = map_( carb_entry_starts, carb_entry_quantities, carb_entry_absorptions, *input_ice, *carb_ratio_tuple, self.INSULIN_SENSITIVITY_START_DATES, self.INSULIN_SENSITIVITY_END_DATES, self.INSULIN_SENSITIVITY_VALUES, default_absorption_times[1] / default_absorption_times[0], default_absorption_times[1], 0 ) self.assertEqual(len(absorptions), 1) self.assertIsNotNone(absorptions[0]) self.assertAlmostEqual(absorptions[0][6], 10488/60, 2) (effect_dates, effect_values ) = dynamic_glucose_effects( carb_entry_starts, carb_entry_quantities, carb_entry_absorptions, absorptions, timelines, *carb_ratio_tuple, self.INSULIN_SENSITIVITY_START_DATES, self.INSULIN_SENSITIVITY_END_DATES, self.INSULIN_SENSITIVITY_VALUES, default_absorption_times[1], delay=10, delta=5, start=input_ice[0][0], end=( input_ice[0][0] + timedelta(hours=18) ) ) assert len(expected_dates) == len(effect_dates) for i in range(0, len(expected_dates)): self.assertEqual( expected_dates[i], effect_dates[i] ) self.assertAlmostEqual( expected_values[i], effect_values[i], 2 )
def test_dynamic_absorption_fully_observed(self): input_ice = self.load_ice_input_fixture("ice_1_hour_input") (carb_starts, carb_values, carb_absorptions ) = self.load_carb_entry_fixture() carb_ratio_tuple = self.load_schedules() default_absorption_times = self.DEFAULT_ABSORPTION_TIMES carb_entry_starts = [carb_starts[0]] carb_entry_quantities = [carb_values[0]] carb_entry_absorptions = [carb_absorptions[0]] (expected_dates, expected_values ) = self.load_cob_output_fixture("ice_1_hour_output") (absorptions, timelines, entries, # pylint: disable=W0612 ) = map_( carb_entry_starts, carb_entry_quantities, carb_entry_absorptions, *input_ice, *carb_ratio_tuple, self.INSULIN_SENSITIVITY_START_DATES, self.INSULIN_SENSITIVITY_END_DATES, self.INSULIN_SENSITIVITY_VALUES, default_absorption_times[1] / default_absorption_times[0], default_absorption_times[1], 0 ) self.assertEqual(len(absorptions), 1) self.assertIsNotNone(absorptions[0]) # No remaining absorption self.assertEqual(absorptions[0][6], 0) # All should be absorbed self.assertEqual(absorptions[0][0], 44) (cob_dates, cob_values ) = dynamic_carbs_on_board( carb_entry_starts, carb_entry_quantities, carb_entry_absorptions, absorptions, timelines, default_absorption_times[1], delay=10, delta=5, start=input_ice[0][0], end=( input_ice[0][0] + timedelta(hours=6) ) ) assert len(expected_dates) == len(cob_dates) for i in range(0, len(expected_dates)): self.assertEqual( expected_dates[i], cob_dates[i] ) self.assertAlmostEqual( expected_values[i], cob_values[i], 1 )
def test_dynamic_absorption_none_observed(self): input_ice = self.load_ice_input_fixture("ice_35_min_input") (carb_starts, carb_values, carb_absorptions ) = self.load_carb_entry_fixture() carb_ratio_tuple = self.load_schedules() default_absorption_times = self.DEFAULT_ABSORPTION_TIMES carb_entry_starts = [carb_starts[2]] carb_entry_quantities = [carb_values[2]] carb_entry_absorptions = [carb_absorptions[2]] (expected_dates, expected_values ) = self.load_cob_output_fixture("ice_35_min_none_output") (absorptions, timelines, entries, # pylint: disable=W0612 ) = map_( carb_entry_starts, carb_entry_quantities, carb_entry_absorptions, *input_ice, *carb_ratio_tuple, self.INSULIN_SENSITIVITY_START_DATES, self.INSULIN_SENSITIVITY_END_DATES, self.INSULIN_SENSITIVITY_VALUES, default_absorption_times[1] / default_absorption_times[0], default_absorption_times[1], 0 ) self.assertEqual(len(absorptions), 1) self.assertEqual(absorptions[0][6], 240) self.assertEqual( absorptions[0][4], datetime.fromisoformat("2015-10-15 23:00:00") ) (cob_dates, cob_values ) = dynamic_carbs_on_board( carb_entry_starts, carb_entry_quantities, carb_entry_absorptions, absorptions, timelines, default_absorption_times[1], delay=10, delta=5, start=input_ice[0][0], end=( input_ice[0][0] + timedelta(hours=6) ) ) assert len(expected_dates) == len(cob_dates) for i in range(0, len(expected_dates)): self.assertEqual( expected_dates[i], cob_dates[i] ) self.assertAlmostEqual( expected_values[i], cob_values[i], 1 )
def get_carb_glucose_effects(carb_dates, carb_values, absorption_times, at_date, effect_starts, effect_ends, effect_values, carb_ratio_starts, carb_ratios, sensitivity_starts, sensitivity_ends, sensitivity_values, default_absorption_times, absorption_time_overrun=1.5, delay=10, delta=5, end_date=None): """ Retrieve a timeline of effect on blood glucose from carbohydrates Arguments: carb_dates -- list of times of carb entry (datetime objects) carb_values -- list of grams of carbs eaten absorption_times -- list of lengths of absorption times (mins) at_date -- the time to calculate the effect at (datetime object) effect_starts -- list of start times of carb effect (datetime objects) effect_ends -- list of end times of carb effect (datetime objects) effect_values -- list of glucose velocities (mg/dL) carb_ratio_starts -- list of start times of carb ratios (time objects) carb_ratios -- list of carb ratios (g/U) sensitivity_starts -- list of time objects of start times of given insulin sensitivity values sensitivity_ends -- list of time objects of start times of given insulin sensitivity values sensitivity_values -- list of sensitivities (mg/dL/U) default_absorption_time -- absorption time to use for unspecified carb entries absorption_time_overrun -- multiplier to determine absorption time from the specified absorption time delay -- the time to delay the carb effect delta -- time interval between glucose values end_date -- date to end calculation of glucose effects Output: An array of effects in chronological order """ assert len(carb_dates) == len(carb_values) == len(absorption_times),\ "expected input shapes to match" assert len(effect_starts) == len(effect_ends) == len(effect_values),\ "expected input shapes to match" if not carb_dates: return ([], []) maximum_absorption_time_interval = default_absorption_times[2] * 2 # To know glucose effects at the requested start date, we need to fetch # samples that might still be absorbing food_start = at_date - timedelta(minutes=maximum_absorption_time_interval) filtered_carbs = filter_date_range_for_carbs(carb_dates, carb_values, absorption_times, food_start, end_date) # if we have counteraction effects, generate our carb glucose effects # with a dynamic model if effect_starts and effect_starts[0]: (absorption_results, timelines) = map_(*filtered_carbs, effect_starts, effect_ends, effect_values, carb_ratio_starts, carb_ratios, sensitivity_starts, sensitivity_ends, sensitivity_values, absorption_time_overrun, default_absorption_times[1], delay, delta)[0:2] effects = dynamic_glucose_effects(*filtered_carbs, absorption_results, timelines, carb_ratio_starts, carb_ratios, sensitivity_starts, sensitivity_ends, sensitivity_values, default_absorption_times[1], delay, delta, start=at_date, end=end_date, scaler=1.7) # otherwise, use a static model else: effects = carb_glucose_effects(*filtered_carbs, carb_ratio_starts, carb_ratios, sensitivity_starts, sensitivity_ends, sensitivity_values, default_absorption_times[1], delay, delta, at_date, end_date) assert len(effects[0]) == len(effects[1]), "expected output shape to match" return effects
def get_carbs_on_board(carb_dates, carb_values, absorption_times, at_date, effect_starts, effect_ends, effect_values, carb_ratio_starts, carb_ratios, sensitivity_starts, sensitivity_ends, sensitivity_values, default_absorption_times, absorption_time_overrun=1.5, delay=10, delta=5, end_date=None): """ Retrieves the COB at a time, or a timeline of COB Arguments: carb_dates -- list of times of carb entry (datetime objects) carb_values -- list of grams of carbs eaten absorption_times -- list of lengths of absorption times (mins) at_date -- the time to calculate the COB at (datetime object) effect_starts -- list of start times of carb effect (datetime objects) effect_ends -- list of end times of carb effect (datetime objects) effect_values -- list of glucose velocities (mg/dL) carb_ratio_starts -- list of start times of carb ratios (time objects) carb_ratios -- list of carb ratios (g/U) sensitivity_starts -- list of time objects of start times of given insulin sensitivity values sensitivity_ends -- list of time objects of start times of given insulin sensitivity values sensitivity_values -- list of sensitivities (mg/dL/U) default_absorption_times -- list absorption times to use for unspecified carb entries in format [fast, medium, slow] absorption_time_overrun -- multiplier to determine absorption time from the specified absorption time delay -- the time to delay the COB delta -- time interval between glucose values end_date -- date to end calculation of COB Output: COB timeline """ assert len(carb_dates) == len(carb_values) == len(absorption_times),\ "expected input shapes to match" assert len(effect_starts) == len(effect_ends) == len(effect_values),\ "expected input shapes to match" if not carb_dates: return ([], []) start_date = at_date - timedelta(minutes=delta) maximum_absorption_time_interval = default_absorption_times[2] * 2 # To know COB at the requested start date, we need to fetch samples that # might still be absorbing food_start = start_date - timedelta( minutes=maximum_absorption_time_interval) filtered_carbs = filter_date_range_for_carbs(carb_dates, carb_values, absorption_times, food_start, end_date) # If we have counteraction effects, use a dynamic model if (effect_starts and effect_starts[0] and carb_ratio_starts and sensitivity_starts): (absorption_results, timelines) = map_(*filtered_carbs, effect_starts, effect_ends, effect_values, carb_ratio_starts, carb_ratios, sensitivity_starts, sensitivity_ends, sensitivity_values, absorption_time_overrun, default_absorption_times[1], delay, delta)[0:2] cob_data = dynamic_carbs_on_board(*filtered_carbs, absorption_results, timelines, default_absorption_times[1], delay, delta, start=start_date, end=end_date, scaler=1.5) # otherwise, use a static model else: cob_data = carbs_on_board(*filtered_carbs, default_absorption_times[1], delay, delta, start=start_date, end=end_date) return cob_data