Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
26 changes: 26 additions & 0 deletions Sources/LoopAlgorithmToPython/LoopAlgorithmToPython.swift
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,25 @@ public func getActiveInsulin(jsonData: UnsafePointer<Int8>?) -> Double {
}
}

@_cdecl("insulinPercentEffectRemaining")
public func insulinPercentEffectRemaining(jsonData: UnsafePointer<Int8>?) -> 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)
Expand Down Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions loop_to_python_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


28 changes: 28 additions & 0 deletions python_tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)


Expand Down Expand Up @@ -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"


Loading