diff --git a/DoseMathTests/DoseMathTests.swift b/DoseMathTests/DoseMathTests.swift index f2c1b495cf..6637784f2b 100644 --- a/DoseMathTests/DoseMathTests.swift +++ b/DoseMathTests/DoseMathTests.swift @@ -347,7 +347,7 @@ class RecommendTempBasalTests: XCTestCase { lastTempBasal: nil ) - XCTAssertEqual(1.425, dose!.unitsPerHour, accuracy: 1.0 / 40.0) + XCTAssertEqual(1.60, dose!.unitsPerHour, accuracy: 1.0 / 40.0) XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration) } @@ -365,7 +365,7 @@ class RecommendTempBasalTests: XCTestCase { lastTempBasal: nil ) - XCTAssertEqual(1.475, dose!.unitsPerHour, accuracy: 1.0 / 40.0) + XCTAssertEqual(1.60, dose!.unitsPerHour, accuracy: 1.0 / 40.0) XCTAssertEqual(TimeInterval(minutes: 30), dose!.duration) } @@ -588,7 +588,7 @@ class RecommendBolusTests: XCTestCase { volumeRounder: fortyIncrementsPerUnitRounder ) - XCTAssertEqual(1.575, dose.amount) + XCTAssertEqual(1.7, dose.amount) if case BolusRecommendationNotice.currentGlucoseBelowTarget(let glucose) = dose.notice! { XCTAssertEqual(glucose.quantity.doubleValue(for: .milligramsPerDeciliter), 60) @@ -657,7 +657,7 @@ class RecommendBolusTests: XCTestCase { volumeRounder: fortyIncrementsPerUnitRounder ) - XCTAssertEqual(1.4, dose.amount) + XCTAssertEqual(1.575, dose.amount) XCTAssertEqual(BolusRecommendationNotice.predictedGlucoseBelowTarget(minGlucose: glucose[1]), dose.notice!) } @@ -676,7 +676,7 @@ class RecommendBolusTests: XCTestCase { volumeRounder: fortyIncrementsPerUnitRounder ) - XCTAssertEqual(0.575, dose.amount) + XCTAssertEqual(0.7, dose.amount) } func testStartVeryLowEndHigh() { @@ -708,7 +708,7 @@ class RecommendBolusTests: XCTestCase { maxBolus: maxBolus ) - XCTAssertEqual(1.575, dose.amount, accuracy: 1.0 / 40.0) + XCTAssertEqual(1.7, dose.amount, accuracy: 1.0 / 40.0) } func testHighAndFalling() { @@ -724,7 +724,7 @@ class RecommendBolusTests: XCTestCase { maxBolus: maxBolus ) - XCTAssertEqual(0.325, dose.amount, accuracy: 1.0 / 40.0) + XCTAssertEqual(0.4, dose.amount, accuracy: 1.0 / 40.0) } func testInRangeAndRising() { @@ -740,7 +740,7 @@ class RecommendBolusTests: XCTestCase { maxBolus: maxBolus ) - XCTAssertEqual(0.325, dose.amount, accuracy: 1.0 / 40.0) + XCTAssertEqual(0.4, dose.amount, accuracy: 1.0 / 40.0) // Less existing temp @@ -771,7 +771,7 @@ class RecommendBolusTests: XCTestCase { volumeRounder: fortyIncrementsPerUnitRounder ) - XCTAssertEqual(0.275, dose.amount) + XCTAssertEqual(0.375, dose.amount) } func testHighAndRising() { @@ -787,7 +787,7 @@ class RecommendBolusTests: XCTestCase { maxBolus: maxBolus ) - XCTAssertEqual(1.25, dose.amount) + XCTAssertEqual(1.35, dose.amount, accuracy: 1.0 / 40.0) // Use mmol sensitivity value let insulinSensitivitySchedule = InsulinSensitivitySchedule(unit: HKUnit.millimolesPerLiter, dailyItems: [RepeatingScheduleValue(startTime: 0.0, value: 10.0 / 3)])! @@ -802,7 +802,7 @@ class RecommendBolusTests: XCTestCase { maxBolus: maxBolus ) - XCTAssertEqual(1.25, dose.amount, accuracy: 1.0 / 40.0) + XCTAssertEqual(1.35, dose.amount, accuracy: 1.0 / 40.0) } func testRiseAfterDIA() { diff --git a/Loop/Managers/DoseMath.swift b/Loop/Managers/DoseMath.swift index 5502084671..b10e4450c0 100644 --- a/Loop/Managers/DoseMath.swift +++ b/Loop/Managers/DoseMath.swift @@ -267,7 +267,8 @@ extension Collection where Element: GlucoseValue { // Compute the dose required to bring this prediction to target: // dose = (Glucose Δ) / (% effect × sensitivity) - let percentEffected = 1 - model.percentEffectRemaining(at: time) + // For 0 <= time <= effectDelay, assume a small amount effected. This will result in large unit recommendation rather than no recommendation at all. + let percentEffected = Swift.max(.ulpOfOne, 1 - model.percentEffectRemaining(at: time)) let effectedSensitivity = percentEffected * sensitivityValue guard let correctionUnits = insulinCorrectionUnits( fromValue: predictedGlucoseValue, @@ -299,7 +300,7 @@ extension Collection where Element: GlucoseValue { eventual.quantity < eventualGlucoseTargets.lowerBound { let time = min.startDate.timeIntervalSince(date) - // For time = 0, assume a small amount effected. This will result in large (negative) unit recommendation rather than no recommendation at all. + // For 0 <= time <= effectDelay, assume a small amount effected. This will result in large (negative) unit recommendation rather than no recommendation at all. let percentEffected = Swift.max(.ulpOfOne, 1 - model.percentEffectRemaining(at: time)) guard let units = insulinCorrectionUnits( diff --git a/LoopCore/Insulin/ExponentialInsulinModelPreset.swift b/LoopCore/Insulin/ExponentialInsulinModelPreset.swift index e2f0877ed8..e65cdd1fb7 100644 --- a/LoopCore/Insulin/ExponentialInsulinModelPreset.swift +++ b/LoopCore/Insulin/ExponentialInsulinModelPreset.swift @@ -38,9 +38,20 @@ extension ExponentialInsulinModelPreset { return .minutes(55) } } + + var effectDelay: TimeInterval { + switch self { + case .humalogNovologAdult: + return .minutes(10) + case .humalogNovologChild: + return .minutes(10) + case .fiasp: + return .minutes(10) + } + } var model: InsulinModel { - return ExponentialInsulinModel(actionDuration: actionDuration, peakActivityTime: peakActivity) + return ExponentialInsulinModel(actionDuration: actionDuration, peakActivityTime: peakActivity, delay: effectDelay) } } @@ -49,6 +60,10 @@ extension ExponentialInsulinModelPreset: InsulinModel { public var effectDuration: TimeInterval { return model.effectDuration } + + public var delay: TimeInterval { + return model.delay + } public func percentEffectRemaining(at time: TimeInterval) -> Double { return model.percentEffectRemaining(at: time)