ExponentialInsulinModel.swift 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //
  2. // ExponentialInsulinModel.swift
  3. // InsulinKit
  4. //
  5. // Created by Pete Schwamb on 7/30/17.
  6. // Copyright © 2017 LoopKit Authors. All rights reserved.
  7. //
  8. import Foundation
  9. public struct ExponentialInsulinModel {
  10. public let actionDuration: TimeInterval
  11. public let peakActivityTime: TimeInterval
  12. public let delay: TimeInterval
  13. // Precomputed terms
  14. fileprivate let τ: Double
  15. fileprivate let a: Double
  16. fileprivate let S: Double
  17. /// Configures a new exponential insulin model
  18. ///
  19. /// - Parameters:
  20. /// - actionDuration: The total duration of insulin activity, excluding delay
  21. /// - peakActivityTime: The time of the peak of insulin activity from dose.
  22. /// - delay: The time to delay the dose effect
  23. public init(actionDuration: TimeInterval, peakActivityTime: TimeInterval, delay: TimeInterval = 600) {
  24. self.actionDuration = actionDuration
  25. self.peakActivityTime = peakActivityTime
  26. self.delay = delay
  27. self.τ = peakActivityTime * (1 - peakActivityTime / actionDuration) / (1 - 2 * peakActivityTime / actionDuration)
  28. self.a = 2 * τ / actionDuration
  29. self.S = 1 / (1 - a + (1 + a) * exp(-actionDuration / τ))
  30. }
  31. }
  32. extension ExponentialInsulinModel: InsulinModel {
  33. public var effectDuration: TimeInterval {
  34. return self.actionDuration + self.delay
  35. }
  36. /// Returns the percentage of total insulin effect remaining at a specified interval after delivery;
  37. /// also known as Insulin On Board (IOB).
  38. ///
  39. /// This is a configurable exponential model as described here: https://github.com/LoopKit/Loop/issues/388#issuecomment-317938473
  40. /// Allows us to specify time of peak activity, as well as duration, and provides activity and IOB decay functions
  41. /// Many thanks to Dragan Maksimovic (@dm61) for creating such a flexible way of adjusting an insulin curve
  42. /// for use in closed loop systems.
  43. ///
  44. /// - Parameter time: The interval after insulin delivery
  45. /// - Returns: The percentage of total insulin effect remaining
  46. public func percentEffectRemaining(at time: TimeInterval) -> Double {
  47. let timeAfterDelay = time - delay
  48. switch timeAfterDelay {
  49. case let t where t <= 0:
  50. return 1
  51. case let t where t >= actionDuration:
  52. return 0
  53. default:
  54. let t = timeAfterDelay
  55. return 1 - S * (1 - a) *
  56. ((pow(t, 2) / (τ * actionDuration * (1 - a)) - t / τ - 1) * exp(-t / τ) + 1)
  57. }
  58. }
  59. }
  60. extension ExponentialInsulinModel: CustomDebugStringConvertible {
  61. public var debugDescription: String {
  62. return "ExponentialInsulinModel(actionDuration: \(actionDuration), peakActivityTime: \(peakActivityTime), delay: \(delay)"
  63. }
  64. }
  65. #if swift(>=4)
  66. extension ExponentialInsulinModel: Decodable {
  67. enum CodingKeys: String, CodingKey {
  68. case actionDuration
  69. case peakActivityTime
  70. case delay
  71. }
  72. public init(from decoder: Decoder) throws {
  73. let container = try decoder.container(keyedBy: CodingKeys.self)
  74. let actionDuration: Double = try container.decode(Double.self, forKey: .actionDuration)
  75. let peakActivityTime: Double = try container.decode(Double.self, forKey: .peakActivityTime)
  76. let delay: Double = try container.decode(TimeInterval.self, forKey: .delay)
  77. self.init(actionDuration: actionDuration, peakActivityTime: peakActivityTime, delay: delay)
  78. }
  79. public func encode(to encoder: Encoder) throws {
  80. var container = encoder.container(keyedBy: CodingKeys.self)
  81. try container.encode(actionDuration, forKey: .actionDuration)
  82. try container.encode(peakActivityTime, forKey: .peakActivityTime)
  83. try container.encode(delay, forKey: .delay)
  84. }
  85. }
  86. #endif