WalshInsulinModel.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. //
  2. // WalshInsulinModel.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 WalshInsulinModel {
  10. public let actionDuration: TimeInterval
  11. public let delay: TimeInterval
  12. public init(actionDuration: TimeInterval, delay: TimeInterval = 600) {
  13. self.actionDuration = actionDuration
  14. self.delay = delay
  15. }
  16. }
  17. extension WalshInsulinModel: InsulinModel {
  18. public var effectDuration: TimeInterval {
  19. return self.actionDuration + self.delay
  20. }
  21. /// Returns the percentage of total insulin effect remaining at a specified interval after delivery;
  22. /// also known as Insulin On Board (IOB).
  23. ///
  24. /// These are 4th-order polynomial fits of John Walsh's IOB curve plots, and they first appeared in GlucoDyn.
  25. ///
  26. /// See: https:github.com/kenstack/GlucoDyn
  27. ///
  28. /// - Parameter time: The interval after insulin delivery
  29. /// - Returns: The percentage of total insulin effect remaining
  30. public func percentEffectRemaining(at time: TimeInterval) -> Double {
  31. let timeAfterDelay = time - delay
  32. switch timeAfterDelay {
  33. case let t where t <= 0:
  34. return 1
  35. case let t where t >= actionDuration:
  36. return 0
  37. default:
  38. // We only have Walsh models for a few discrete action durations, so we scale other action durations appropriately to the nearest one.
  39. let nearestModeledDuration: TimeInterval
  40. switch actionDuration {
  41. case let x where x < TimeInterval(hours: 3):
  42. nearestModeledDuration = TimeInterval(hours: 3)
  43. case let x where x > TimeInterval(hours: 6):
  44. nearestModeledDuration = TimeInterval(hours: 6)
  45. default:
  46. nearestModeledDuration = TimeInterval(hours: round(actionDuration.hours))
  47. }
  48. let minutes = timeAfterDelay.minutes * nearestModeledDuration / actionDuration
  49. switch nearestModeledDuration {
  50. case TimeInterval(hours: 3):
  51. return -3.2030e-9 * pow(minutes, 4) + 1.354e-6 * pow(minutes, 3) - 1.759e-4 * pow(minutes, 2) + 9.255e-4 * minutes + 0.99951
  52. case TimeInterval(hours: 4):
  53. return -3.310e-10 * pow(minutes, 4) + 2.530e-7 * pow(minutes, 3) - 5.510e-5 * pow(minutes, 2) - 9.086e-4 * minutes + 0.99950
  54. case TimeInterval(hours: 5):
  55. return -2.950e-10 * pow(minutes, 4) + 2.320e-7 * pow(minutes, 3) - 5.550e-5 * pow(minutes, 2) + 4.490e-4 * minutes + 0.99300
  56. case TimeInterval(hours: 6):
  57. return -1.493e-10 * pow(minutes, 4) + 1.413e-7 * pow(minutes, 3) - 4.095e-5 * pow(minutes, 2) + 6.365e-4 * minutes + 0.99700
  58. default:
  59. assertionFailure()
  60. return 0
  61. }
  62. }
  63. }
  64. }
  65. extension WalshInsulinModel: CustomDebugStringConvertible {
  66. public var debugDescription: String {
  67. return "WalshInsulinModel(actionDuration: \(actionDuration), delay: \(delay))"
  68. }
  69. }
  70. extension WalshInsulinModel: Equatable {
  71. public static func ==(lhs: WalshInsulinModel, rhs: WalshInsulinModel) -> Bool {
  72. return abs(lhs.actionDuration - rhs.actionDuration) < .ulpOfOne
  73. }
  74. }
  75. #if swift(>=4)
  76. extension WalshInsulinModel: Codable {
  77. enum CodingKeys: String, CodingKey {
  78. case actionDuration
  79. case delay
  80. }
  81. public init(from decoder: Decoder) throws {
  82. let container = try decoder.container(keyedBy: CodingKeys.self)
  83. let actionDuration: Double = try container.decode(Double.self, forKey: .actionDuration)
  84. let delay: Double = try container.decode(TimeInterval.self, forKey: .delay)
  85. self.init(actionDuration: actionDuration, delay: delay)
  86. }
  87. public func encode(to encoder: Encoder) throws {
  88. var container = encoder.container(keyedBy: CodingKeys.self)
  89. try container.encode(actionDuration, forKey: .actionDuration)
  90. try container.encode(delay, forKey: .delay)
  91. }
  92. }
  93. #endif
  94. extension WalshInsulinModel: RawRepresentable {
  95. public typealias RawValue = [String: Any]
  96. public init?(rawValue: RawValue) {
  97. guard let duration = rawValue["actionDuration"] as? TimeInterval else {
  98. return nil
  99. }
  100. self.init(actionDuration: duration)
  101. }
  102. public var rawValue: [String : Any] {
  103. return ["actionDuration": self.actionDuration]
  104. }
  105. }