import CoreModels public enum HeatingBalancePoint { public static let description: String = """ Calculate the heating balance point. """ public enum Mode: String, CaseIterable, Codable, Equatable, Sendable, Hashable { case economic case thermal } public enum Request: Codable, Equatable, Sendable, Hashable { case economic(Economic) case thermal(Thermal) public struct Economic: Codable, Equatable, Sendable, Hashable { public let fuelType: FuelType public let fuelCostPerUnit: Double public let fuelAFUE: Double public let costPerKW: Double public init( fuelType: HeatingBalancePoint.FuelType, fuelCostPerUnit: Double, fuelAFUE: Double, costPerKW: Double ) { self.fuelType = fuelType self.fuelCostPerUnit = fuelCostPerUnit self.fuelAFUE = fuelAFUE self.costPerKW = costPerKW } } public struct Thermal: Codable, Equatable, Sendable, Hashable { public let systemSize: Double public let capacityAt47: Double? public let capacityAt17: Double? public let heatingDesignTemperature: Double? public let buildingHeatLoss: HeatingBalancePoint.HeatLoss public let climateZone: ClimateZone? public init( systemSize: Double, capacityAt47: Double? = nil, capacityAt17: Double? = nil, heatingDesignTemperature: Double? = nil, buildingHeatLoss: HeatingBalancePoint.HeatLoss, climateZone: ClimateZone? = nil ) { self.systemSize = systemSize self.capacityAt47 = capacityAt47 self.capacityAt17 = capacityAt17 self.heatingDesignTemperature = heatingDesignTemperature self.buildingHeatLoss = buildingHeatLoss self.climateZone = climateZone } } } public enum Response: Codable, Equatable, Sendable { case economic(Economic) case thermal(Thermal) public struct Economic: Codable, Equatable, Sendable { public let balancePointTemperature: Double public let fuelCostPerMMBTU: Double public let electricCostPerMMBTU: Double public let copAtBalancePoint: Double public let electricFuelRatio: Double public init( balancePointTemperature: Double, fuelCostPerMMBTU: Double, electricCostPerMMBTU: Double, copAtBalancePoint: Double, electricFuelRatio: Double ) { self.balancePointTemperature = balancePointTemperature self.fuelCostPerMMBTU = fuelCostPerMMBTU self.electricCostPerMMBTU = electricCostPerMMBTU self.copAtBalancePoint = copAtBalancePoint self.electricFuelRatio = electricFuelRatio } } public struct Thermal: Codable, Equatable, Sendable { public let capacityAt47: Double public let capacityAt17: Double public let balancePointTemperature: Double public let heatLoss: Double public let heatLossMode: HeatLoss.Mode public let heatingDesignTemperature: Double public let warnings: [String] public init( capacityAt47: Double, capacityAt17: Double, balancePointTemperature: Double, heatLoss: Double, heatLossMode: HeatLoss.Mode, heatingDesignTemperature: Double, warnings: [String] ) { self.capacityAt47 = capacityAt47 self.capacityAt17 = capacityAt17 self.balancePointTemperature = balancePointTemperature self.heatLoss = heatLoss self.heatLossMode = heatLossMode self.heatingDesignTemperature = heatingDesignTemperature self.warnings = warnings } } } public enum HeatLoss: Codable, Equatable, Sendable, Hashable { public enum Mode: String, CaseIterable, Codable, Equatable, Sendable, Hashable { case estimated case known } case known(btu: Double) case estimated(squareFeet: Double) public var mode: Mode { switch self { case .known: return .known case .estimated: return .estimated } } } public enum FuelType: String, CaseIterable, Codable, Equatable, Sendable, Hashable { case naturalGas case propane case oil public var label: String { switch self { case .propane, .oil: return "\(rawValue.capitalized)" case .naturalGas: return "Natural Gas" } } public var units: String { switch self { case .propane, .oil: return "gallons" case .naturalGas: return "therm" } } } } #if DEBUG public extension HeatingBalancePoint.Response { static func mock(mode: HeatingBalancePoint.Mode) -> Self { switch mode { case .economic: return .economic(.init( balancePointTemperature: -10.8, fuelCostPerMMBTU: 27.6, electricCostPerMMBTU: 38.1, copAtBalancePoint: 2.24, electricFuelRatio: 1.38 )) case .thermal: return .thermal(.init( capacityAt47: 24600, capacityAt17: 15100, balancePointTemperature: 38.5, heatLoss: 49667, heatLossMode: .known, heatingDesignTemperature: 5, warnings: [ "Design temperature is estimated based on climate zone." ] )) } } } #endif