From 54ef1d57c7ba756d6690208a190321e91666c524 Mon Sep 17 00:00:00 2001 From: "Miriam K. Wolff" Date: Thu, 11 Sep 2025 12:47:37 +0200 Subject: [PATCH 1/2] Added a new command for the insulin percent effect remaining --- README.md | 25 ++++++++++++++++++ .../LoopAlgorithmToPython.swift | 26 +++++++++++++++++++ loop_to_python_api/api.py | 25 ++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/README.md b/README.md index d930708..9f1327d 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,31 @@ Fetches the active insulin based on the provided JSON input. ------------------------- +### Insulin Percent Effect Remaining + +`insulin_percent_effect_remaining(minutes, action_duration, peak_activity_time, delay)` + +Calculates the percentage of insulin effect remaining at a given time point using the exponential insulin model. + +- **Parameters**: + - `minutes`: Time point in minutes to calculate effect remaining + - `action_duration`: Total duration of insulin action in minutes + - `peak_activity_time`: Time to peak insulin activity in minutes + - `delay`: Delay before insulin activity begins in minutes +- **Returns**: Percentage of insulin effect remaining (0.0 to 1.0) +- **Example**: +```python +# Calculate remaining effect at 60 minutes for typical rapid-acting insulin +remaining = insulin_percent_effect_remaining( + minutes=60, # 60 minutes after injection + action_duration=360, # 6 hours total duration + peak_activity_time=75, # Peak at 75 minutes + delay=10 # 10 minute delay +) +``` + +------------------------- + ### Add Insulin Counteraction Effect to DataFrame diff --git a/Sources/LoopAlgorithmToPython/LoopAlgorithmToPython.swift b/Sources/LoopAlgorithmToPython/LoopAlgorithmToPython.swift index f53922d..9cf4c9b 100644 --- a/Sources/LoopAlgorithmToPython/LoopAlgorithmToPython.swift +++ b/Sources/LoopAlgorithmToPython/LoopAlgorithmToPython.swift @@ -265,6 +265,25 @@ public func getActiveInsulin(jsonData: UnsafePointer?) -> Double { } } +@_cdecl("insulinPercentEffectRemaining") +public func insulinPercentEffectRemaining(jsonData: UnsafePointer?) -> Double { + let data = getDataFromJson(jsonData: jsonData) + + do { + let input = try getDecoder().decode(InsulinPercentEffectInput.self, from: data) + + let actionDuration = TimeInterval(input.actionDuration * 60) + let peakActivityTime = TimeInterval(input.peakActivityTime * 60) + let delay = TimeInterval(input.delay * 60) + let minutes = TimeInterval(input.minutes * 60) + + let model = ExponentialInsulinModel(actionDuration: actionDuration, peakActivityTime: peakActivityTime, delay: delay) + return model.percentEffectRemaining(at: minutes) + } catch { + fatalError("Error reading or decoding JSON file: \(error)") + } +} + @_cdecl("percentAbsorptionAtPercentTime") public func percentAbsorptionAtPercentTime(_ percentTime: Double) -> Double { return PiecewiseLinearAbsorption().percentAbsorptionAtPercentTime(percentTime) @@ -341,6 +360,13 @@ public struct DynamicCarbsData: Codable { let carbRatio: Double } +public struct InsulinPercentEffectInput: Codable { + let minutes: Double + let actionDuration: Double + let peakActivityTime: Double + let delay: Double +} + struct CarbValue: Codable { let grams: Int let absorptionTime: Int diff --git a/loop_to_python_api/api.py b/loop_to_python_api/api.py index 2b897c5..f7864d8 100644 --- a/loop_to_python_api/api.py +++ b/loop_to_python_api/api.py @@ -235,3 +235,28 @@ def get_dynamic_carbs_on_board(json_file): return swift_lib.getDynamicCarbsOnBoard(json_bytes) +def insulin_percent_effect_remaining(minutes, action_duration, peak_activity_time, delay): + """ + Calculate the percentage of insulin effect remaining at a given time point. + + :param minutes: Time point in minutes to calculate effect remaining + :param action_duration: Total duration of insulin action in minutes + :param peak_activity_time: Time to peak insulin activity in minutes + :param delay: Delay before insulin activity begins in minutes + :return: Percentage of insulin effect remaining (0.0 to 1.0) + """ + input_data = { + "minutes": minutes, + "actionDuration": action_duration, + "peakActivityTime": peak_activity_time, + "delay": delay + } + + json_bytes = helpers.get_bytes_from_json(input_data) + + swift_lib.insulinPercentEffectRemaining.argtypes = [ctypes.c_char_p] + swift_lib.insulinPercentEffectRemaining.restype = ctypes.c_double + + return swift_lib.insulinPercentEffectRemaining(json_bytes) + + From 0e186669c508e8da89dd464993084d26d5f24369 Mon Sep 17 00:00:00 2001 From: "Miriam K. Wolff" Date: Thu, 11 Sep 2025 12:51:20 +0200 Subject: [PATCH 2/2] Add test perc insulin remaining --- python_tests/tests.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/python_tests/tests.py b/python_tests/tests.py index d352cb6..35140e5 100644 --- a/python_tests/tests.py +++ b/python_tests/tests.py @@ -15,6 +15,7 @@ piecewise_linear_percent_rate_at_percent_time, linear_percent_rate_at_percent_time, get_dynamic_carbs_on_board, + insulin_percent_effect_remaining, ) @@ -122,3 +123,30 @@ def test_get_dynamic_carbs_on_board(): assert isinstance(dynamic_carbs_on_board, float) +def test_insulin_percent_effect_remaining(): + # Test with typical rapid-acting insulin parameters + result = insulin_percent_effect_remaining( + minutes=60, # 60 minutes after injection + action_duration=360, # 6 hours total duration + peak_activity_time=75, # Peak at 75 minutes + delay=10 # 10 minute delay + ) + + # Basic assertions + assert isinstance(result, float), "Result should be a float" + assert 0.0 <= result <= 1.0, f"Result should be between 0.0 and 1.0, got {result}" + + # Test edge cases + # At time 0 (before delay), should have close to 100% effect remaining + result_start = insulin_percent_effect_remaining(0, 360, 75, 10) + assert result_start > 0.9, f"At start, should have >90% remaining, got {result_start}" + + # At very end of action duration, should have very little effect remaining + result_end = insulin_percent_effect_remaining(360, 360, 75, 10) + assert result_end < 0.1, f"At end, should have <10% remaining, got {result_end}" + + # At peak time, should have less than 100% but more than end + result_peak = insulin_percent_effect_remaining(75, 360, 75, 10) + assert result_end < result_peak < result_start, "Effect should decrease over time" + +