Skip to content

Commit

Permalink
If minBG is below 55, then shut off basal, even if eventualBG is in r…
Browse files Browse the repository at this point in the history
…ange. Simplify code by removing allowPredictiveTempBelowRange flag.
  • Loading branch information
ps2 committed Dec 9, 2016
1 parent 370bea1 commit d4bdf59
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 127 deletions.
161 changes: 40 additions & 121 deletions DoseMathTests/DoseMathTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertNil(dose)
Expand All @@ -113,8 +112,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertNil(dose)
Expand All @@ -134,8 +132,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(0, dose!.rate)
Expand All @@ -151,21 +148,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
)

XCTAssertEqual(0, dose!.rate)
XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)

dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: nil,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: true
basalRateSchedule: basalRateSchedule
)

XCTAssertNil(dose)
Expand All @@ -184,8 +167,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: true
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(0, dose!.rate)
Expand All @@ -195,83 +177,26 @@ class RecommendTempBasalTests: XCTestCase {
func testCorrectLowAtMin() {
let glucose = loadGlucoseValueFixture("recommend_temp_basal_correct_low_at_min")

var dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: nil,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
)

XCTAssertEqualWithAccuracy(0.125, dose!.rate, accuracy: 1.0 / 40.0)
XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)

// Ignore due to existing dose
var lastTempBasal = DoseEntry(
type: .tempBasal,
startDate: glucose.first!.startDate.addingTimeInterval(TimeInterval(minutes: -11)),
endDate: glucose.first!.startDate.addingTimeInterval(TimeInterval(minutes: 19)),
value: 0.125,
unit: .unitsPerHour
)

dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: lastTempBasal,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
)

XCTAssertNil(dose)

// Cancel existing dose
lastTempBasal = DoseEntry(
type: .tempBasal,
startDate: glucose.first!.startDate.addingTimeInterval(TimeInterval(minutes: -11)),
endDate: glucose.first!.startDate.addingTimeInterval(TimeInterval(minutes: 19)),
value: 1.225,
unit: .unitsPerHour
)

dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: lastTempBasal,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
)

XCTAssertEqualWithAccuracy(0.125, dose!.rate, accuracy: 1.0 / 40.0)
XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)

// Continue existing dose
lastTempBasal = DoseEntry(
var lastTempBasal = DoseEntry(
type: .tempBasal,
startDate: glucose.first!.startDate.addingTimeInterval(TimeInterval(minutes: -21)),
endDate: glucose.first!.startDate.addingTimeInterval(TimeInterval(minutes: 9)),
value: 0.125,
unit: .unitsPerHour
)

dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
var dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: lastTempBasal,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqualWithAccuracy(0.125, dose!.rate, accuracy: 1.0 / 40.0)
XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)
XCTAssertEqual(0, dose!.rate)
XCTAssertEqual(TimeInterval(minutes: 0), dose!.duration)

// Allow predictive temp below range
dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
Expand All @@ -280,8 +205,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: true
basalRateSchedule: basalRateSchedule
)

XCTAssertNil(dose)
Expand All @@ -300,8 +224,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: true
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(0, dose!.rate)
Expand All @@ -317,8 +240,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(0, dose!.rate)
Expand All @@ -328,28 +250,14 @@ class RecommendTempBasalTests: XCTestCase {
func testStartLowEndHigh() {
let glucose = loadGlucoseValueFixture("recommend_temp_basal_start_low_end_high")

var dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: nil,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
)

XCTAssertEqual(0, dose!.rate)
XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)

// Allow predictive temp below range
dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
var dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: nil,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: true
basalRateSchedule: basalRateSchedule
)

XCTAssertNil(dose)
Expand All @@ -368,8 +276,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: true
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(0, dose!.rate)
Expand All @@ -385,8 +292,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(3.0, dose!.rate)
Expand All @@ -402,8 +308,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqualWithAccuracy(1.425, dose!.rate, accuracy: 1.0 / 40.0)
Expand All @@ -419,8 +324,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqualWithAccuracy(1.475, dose!.rate, accuracy: 1.0 / 40.0)
Expand All @@ -436,8 +340,7 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: self.insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(3.0, dose!.rate)
Expand All @@ -452,22 +355,37 @@ class RecommendTempBasalTests: XCTestCase {
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertEqualWithAccuracy(2.975, dose!.rate, accuracy: 1.0 / 40.0)
XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)
}

func testVeryLowAndRising() {
let glucose = loadGlucoseValueFixture("recommend_tamp_basal_very_low_end_in_range")

let dose = DoseMath.recommendTempBasalFromPredictedGlucose(glucose,
atDate: glucose.first!.startDate,
lastTempBasal: nil,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: self.insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule
)

XCTAssertEqual(0.0, dose!.rate)
XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration)
}


func testNoInputGlucose() {
let dose = DoseMath.recommendTempBasalFromPredictedGlucose([],
lastTempBasal: nil,
maxBasalRate: maxBasalRate,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivitySchedule,
basalRateSchedule: basalRateSchedule,
allowPredictiveTempBelowRange: false
basalRateSchedule: basalRateSchedule
)

XCTAssertNil(dose)
Expand Down Expand Up @@ -728,4 +646,5 @@ class RecommendBolusTests: XCTestCase {

XCTAssertEqual(0, dose)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{"date": "2015-07-19T18:00:00", "amount": 60},
{"date": "2015-07-19T18:30:00", "amount": 50},
{"date": "2015-07-19T19:00:00", "amount": 60},
{"date": "2015-07-19T19:30:00", "amount": 70},
{"date": "2015-07-19T20:00:00", "amount": 100}
]
4 changes: 4 additions & 0 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
4D5B7A4B1D457CCA00796CA9 /* GlucoseG4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D5B7A4A1D457CCA00796CA9 /* GlucoseG4.swift */; };
4F526D5D1DF0FD6500A04910 /* InsulinKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 43C6407B1DA051850093E25D /* InsulinKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C10428971D17BAD400DD539A /* NightscoutUploadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C10428961D17BAD400DD539A /* NightscoutUploadKit.framework */; };
C12F21A71DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json in Resources */ = {isa = PBXBuildFile; fileRef = C12F21A61DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json */; };
C15713821DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15713811DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift */; };
C17884631D51A7A400405663 /* BatteryIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17884621D51A7A400405663 /* BatteryIndicator.swift */; };
C18C8C511D5A351900E043FB /* NightscoutDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */; };
Expand Down Expand Up @@ -392,6 +393,7 @@
4D3B40021D4A9DFE00BC6334 /* G4ShareSpy.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = G4ShareSpy.framework; path = Carthage/Build/iOS/G4ShareSpy.framework; sourceTree = "<group>"; };
4D5B7A4A1D457CCA00796CA9 /* GlucoseG4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GlucoseG4.swift; path = Loop/Models/GlucoseG4.swift; sourceTree = SOURCE_ROOT; };
C10428961D17BAD400DD539A /* NightscoutUploadKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NightscoutUploadKit.framework; path = Carthage/Build/iOS/NightscoutUploadKit.framework; sourceTree = "<group>"; };
C12F21A61DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = recommend_tamp_basal_very_low_end_in_range.json; sourceTree = "<group>"; };
C15713811DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MealBolusNightscoutTreatment.swift; sourceTree = "<group>"; };
C17884621D51A7A400405663 /* BatteryIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryIndicator.swift; sourceTree = "<group>"; };
C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutDataManager.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -634,6 +636,7 @@
43E2D8E91D20C0DB004DA55F /* recommend_temp_basal_start_high_end_low.json */,
43E2D8EA1D20C0DB004DA55F /* recommend_temp_basal_start_low_end_high.json */,
43E2D8EB1D20C0DB004DA55F /* recommend_temp_basal_start_low_end_in_range.json */,
C12F21A61DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json */,
);
path = Fixtures;
sourceTree = "<group>";
Expand Down Expand Up @@ -990,6 +993,7 @@
43E2D8EF1D20C0DB004DA55F /* recommend_temp_basal_high_and_falling.json in Resources */,
43E2D8ED1D20C0DB004DA55F /* recommend_temp_basal_correct_low_at_min.json in Resources */,
43E2D8F01D20C0DB004DA55F /* recommend_temp_basal_high_and_rising.json in Resources */,
C12F21A71DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json in Resources */,
43E2D8F11D20C0DB004DA55F /* recommend_temp_basal_in_range_and_rising.json in Resources */,
43E2D8EE1D20C0DB004DA55F /* recommend_temp_basal_flat_and_high.json in Resources */,
43E2D8F31D20C0DB004DA55F /* recommend_temp_basal_start_high_end_in_range.json in Resources */,
Expand Down
10 changes: 6 additions & 4 deletions Loop/Managers/DoseMath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ struct DoseMath {
- parameter glucoseTargetRange: The schedule of target glucose ranges
- parameter insulinSensitivity: The schedule of insulin sensitivities, in Units of insulin per glucose-unit
- parameter basalRateSchedule: The schedule of basal rates
- parameter allowPredictiveTempBelowRange: Whether to allow a higher basal rate, up to the normal scheduled rate, than is necessary to correct the lowest predicted value, if the eventual predicted value is in or above the target range. Defaults to false.

- returns: The recommended basal rate and duration
*/
Expand All @@ -61,8 +60,7 @@ struct DoseMath {
maxBasalRate: Double,
glucoseTargetRange: GlucoseRangeSchedule,
insulinSensitivity: InsulinSensitivitySchedule,
basalRateSchedule: BasalRateSchedule,
allowPredictiveTempBelowRange: Bool
basalRateSchedule: BasalRateSchedule
) -> (rate: Double, duration: TimeInterval)? {
guard glucose.count > 1 else {
return nil
Expand All @@ -79,7 +77,11 @@ struct DoseMath {
var rate: Double?
var duration = TimeInterval(minutes: 30)

if minGlucose.quantity.doubleValue(for: glucoseTargetRange.unit) < minGlucoseTargets.minValue && (!allowPredictiveTempBelowRange || eventualGlucose.quantity.doubleValue(for: glucoseTargetRange.unit) <= eventualGlucoseTargets.minValue) {
let alwaysLowTempBGThreshold: Double = 55 // mg/dL

if minGlucose.quantity.doubleValue(for: HKUnit.milligramsPerDeciliterUnit()) <= alwaysLowTempBGThreshold {
rate = 0
} else if minGlucose.quantity.doubleValue(for: glucoseTargetRange.unit) < minGlucoseTargets.minValue && eventualGlucose.quantity.doubleValue(for: glucoseTargetRange.unit) <= eventualGlucoseTargets.minValue {
let targetGlucose = HKQuantity(unit: glucoseTargetRange.unit, doubleValue: (minGlucoseTargets.minValue + minGlucoseTargets.maxValue) / 2)
rate = calculateTempBasalRateForGlucose(minGlucose.quantity,
toTargetGlucose: targetGlucose,
Expand Down
3 changes: 1 addition & 2 deletions Loop/Managers/LoopDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -545,8 +545,7 @@ final class LoopDataManager {
maxBasalRate: maxBasal,
glucoseTargetRange: glucoseTargetRange,
insulinSensitivity: insulinSensitivity,
basalRateSchedule: basalRates,
allowPredictiveTempBelowRange: true
basalRateSchedule: basalRates
)
else {
recommendedTempBasal = nil
Expand Down

0 comments on commit d4bdf59

Please sign in to comment.