This commit is contained in:
61
Sources/ManualS/Internal/ProposedKWInterpolation.swift
Normal file
61
Sources/ManualS/Internal/ProposedKWInterpolation.swift
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import CoreFoundation
|
||||||
|
import Models
|
||||||
|
import Validations
|
||||||
|
|
||||||
|
extension ProposedKWInterpolation.Request {
|
||||||
|
|
||||||
|
private static let oversizingLimit = 175
|
||||||
|
|
||||||
|
func respond() async throws -> ProposedKWInterpolation.Response {
|
||||||
|
try await validate()
|
||||||
|
let (requiredKW, optionalHeatPump) = try await requiredKW()
|
||||||
|
let capacityAsPercentOfLoad = normalizePercentage(proposedKW, requiredKW)
|
||||||
|
|
||||||
|
var failures = [String]()
|
||||||
|
if capacityAsPercentOfLoad > Self.oversizingLimit {
|
||||||
|
failures.append("Oversizing failure.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return .init(
|
||||||
|
failures: failures.isEmpty ? nil : failures,
|
||||||
|
supplementalHeatRequired: requiredKW,
|
||||||
|
capacityAsPercentOfLoad: capacityAsPercentOfLoad,
|
||||||
|
oversizingLimit: Self.oversizingLimit,
|
||||||
|
altitudeAdjustmentMultiplier: optionalHeatPump?.altitudeAdjustmentMultiplier,
|
||||||
|
balancePointTemperature: optionalHeatPump?.balancePointTemperature,
|
||||||
|
capacityAtDesign: optionalHeatPump?.capacityAtDesign,
|
||||||
|
finalCapacity: optionalHeatPump?.finalCapacity
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var heatPumpRequest: HeatPumpHeatingInterpolation.Request? {
|
||||||
|
guard let winterDesignTemperature, let climateType, let capacity else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return .init(
|
||||||
|
winterDesignTemperature: winterDesignTemperature,
|
||||||
|
heatLoss: heatLoss,
|
||||||
|
climateType: climateType,
|
||||||
|
capacity: capacity
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func requiredKW() async throws -> (Int, HeatPumpHeatingInterpolation.Response?) {
|
||||||
|
guard let heatPumpRequest = heatPumpRequest else {
|
||||||
|
let requiredKW = try await RequiredKW.Request(heatLoss: heatLoss).respond()
|
||||||
|
return (Int(requiredKW.requiredKW), nil)
|
||||||
|
}
|
||||||
|
let heatPumpResponse = try await heatPumpRequest.respond()
|
||||||
|
return (heatPumpResponse.supplementalHeatRequired, heatPumpResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProposedKWInterpolation.Request: AsyncValidatable {
|
||||||
|
public var body: some AsyncValidation<Self> {
|
||||||
|
AsyncValidator.accumulating {
|
||||||
|
AsyncValidator.greaterThan(\.heatLoss, 0)
|
||||||
|
AsyncValidator.greaterThan(\.proposedKW, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,35 +12,39 @@ public extension DependencyValues {
|
|||||||
@DependencyClient
|
@DependencyClient
|
||||||
public struct ManualS: Sendable {
|
public struct ManualS: Sendable {
|
||||||
public var balancePoint: @Sendable (BalancePoint.Request) async throws -> BalancePoint.Response
|
public var balancePoint: @Sendable (BalancePoint.Request) async throws -> BalancePoint.Response
|
||||||
|
|
||||||
public var derating: @Sendable (Derating.Request) async throws -> Derating.Response
|
public var derating: @Sendable (Derating.Request) async throws -> Derating.Response
|
||||||
|
public var interpolate: Interpolations
|
||||||
|
public var requiredKW: @Sendable (RequiredKW.Request) async throws -> RequiredKW.Response
|
||||||
|
public var sizingLimits: @Sendable (SizingLimits.Request) async throws -> SizingLimits.Response
|
||||||
|
}
|
||||||
|
|
||||||
public var coolingInterpolation:
|
@DependencyClient
|
||||||
@Sendable (CoolingInterpolation.Request) async throws -> CoolingInterpolation.Response
|
public struct Interpolations: Sendable {
|
||||||
|
public var cooling: @Sendable (CoolingInterpolation.Request) async throws -> CoolingInterpolation.Response
|
||||||
|
|
||||||
public var furnaceInterpolation:
|
public var furnace: @Sendable (FurnaceInterpolation.Request) async throws -> FurnaceInterpolation.Response
|
||||||
@Sendable (FurnaceInterpolation.Request) async throws -> FurnaceInterpolation.Response
|
|
||||||
|
|
||||||
public var heatPumpHeatingInterpolation:
|
public var heatPumpHeating:
|
||||||
@Sendable (HeatPumpHeatingInterpolation.Request) async throws -> HeatPumpHeatingInterpolation.Response
|
@Sendable (HeatPumpHeatingInterpolation.Request) async throws -> HeatPumpHeatingInterpolation.Response
|
||||||
|
|
||||||
public var requiredKW: @Sendable (RequiredKW.Request) async throws -> RequiredKW.Response
|
public var proposeKW: @Sendable (ProposedKWInterpolation.Request) async throws -> ProposedKWInterpolation.Response
|
||||||
|
|
||||||
public var sizingLimits: @Sendable (SizingLimits.Request) async throws -> SizingLimits.Response
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ManualS: DependencyKey {
|
extension ManualS: DependencyKey {
|
||||||
public static let liveValue = Self(
|
public static let liveValue = Self(
|
||||||
balancePoint: { try await $0.respond() },
|
balancePoint: { try await $0.respond() },
|
||||||
derating: { try await $0.respond() },
|
derating: { try await $0.respond() },
|
||||||
coolingInterpolation: { try await $0.respond() },
|
interpolate: .init(
|
||||||
furnaceInterpolation: { try await $0.respond() },
|
cooling: { try await $0.respond() },
|
||||||
heatPumpHeatingInterpolation: { try await $0.respond() },
|
furnace: { try await $0.respond() },
|
||||||
|
heatPumpHeating: { try await $0.respond() },
|
||||||
|
proposeKW: { try await $0.respond() }
|
||||||
|
),
|
||||||
requiredKW: { try await $0.respond() },
|
requiredKW: { try await $0.respond() },
|
||||||
sizingLimits: { try await $0.respond() }
|
sizingLimits: { try await $0.respond() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ManualS: TestDependencyKey {
|
extension ManualS: TestDependencyKey {
|
||||||
public static let testValue: ManualS = Self()
|
public static let testValue: ManualS = Self(interpolate: .init())
|
||||||
}
|
}
|
||||||
|
|||||||
66
Sources/Models/ProposedKWInterpolation.swift
Normal file
66
Sources/Models/ProposedKWInterpolation.swift
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
public enum ProposedKWInterpolation {
|
||||||
|
public struct Request: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
|
public let elevation: Int?
|
||||||
|
|
||||||
|
// These are required if also doing a heat pump heating interpolation.
|
||||||
|
public let winterDesignTemperature: Int?
|
||||||
|
public let climateType: SystemType.ClimateType?
|
||||||
|
public let capacity: Capacity.HeatPumpHeating?
|
||||||
|
|
||||||
|
public let heatLoss: Int
|
||||||
|
public let proposedKW: Int
|
||||||
|
|
||||||
|
public init(
|
||||||
|
elevation: Int? = nil,
|
||||||
|
winterDesignTemperature: Int? = nil,
|
||||||
|
climateType: SystemType.ClimateType? = nil,
|
||||||
|
capacity: Capacity.HeatPumpHeating? = nil,
|
||||||
|
heatLoss: Int,
|
||||||
|
proposedKW: Int
|
||||||
|
) {
|
||||||
|
self.elevation = elevation
|
||||||
|
self.winterDesignTemperature = winterDesignTemperature
|
||||||
|
self.climateType = climateType
|
||||||
|
self.capacity = capacity
|
||||||
|
self.heatLoss = heatLoss
|
||||||
|
self.proposedKW = proposedKW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Response: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
|
public let failed: Bool
|
||||||
|
public let failures: [String]?
|
||||||
|
public let supplementalHeatRequired: Int
|
||||||
|
public let capacityAsPercentOfLoad: Int
|
||||||
|
public let oversizingLimit: Int
|
||||||
|
|
||||||
|
// These are if a heat pump interpolation is also done.
|
||||||
|
public let altitudeAdjustmentMultiplier: Double?
|
||||||
|
public let balancePointTemperature: Int?
|
||||||
|
public let capacityAtDesign: Int?
|
||||||
|
public let finalCapacity: Capacity.HeatPumpHeating?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
failures: [String]? = nil,
|
||||||
|
supplementalHeatRequired: Int,
|
||||||
|
capacityAsPercentOfLoad: Int,
|
||||||
|
oversizingLimit: Int,
|
||||||
|
altitudeAdjustmentMultiplier: Double? = nil,
|
||||||
|
balancePointTemperature: Int? = nil,
|
||||||
|
capacityAtDesign: Int? = nil,
|
||||||
|
finalCapacity: Capacity.HeatPumpHeating? = nil
|
||||||
|
) {
|
||||||
|
self.failed = failures == nil ? false : !failures!.isEmpty
|
||||||
|
self.failures = failures
|
||||||
|
self.supplementalHeatRequired = supplementalHeatRequired
|
||||||
|
self.capacityAsPercentOfLoad = capacityAsPercentOfLoad
|
||||||
|
self.oversizingLimit = oversizingLimit
|
||||||
|
self.altitudeAdjustmentMultiplier = altitudeAdjustmentMultiplier
|
||||||
|
self.balancePointTemperature = balancePointTemperature
|
||||||
|
self.capacityAtDesign = capacityAtDesign
|
||||||
|
self.finalCapacity = finalCapacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ struct ManualSTests {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.manualS) var manualS
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
let response = try await manualS.coolingInterpolation(.init(
|
let response = try await manualS.interpolate.cooling(.init(
|
||||||
summerDesignInfo: .mock,
|
summerDesignInfo: .mock,
|
||||||
coolingLoad: .mockCoolingLoad,
|
coolingLoad: .mockCoolingLoad,
|
||||||
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
@@ -48,7 +48,7 @@ struct ManualSTests {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.manualS) var manualS
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
let response = try await manualS.coolingInterpolation(.init(
|
let response = try await manualS.interpolate.cooling(.init(
|
||||||
summerDesignInfo: .mock,
|
summerDesignInfo: .mock,
|
||||||
coolingLoad: .mockCoolingLoad,
|
coolingLoad: .mockCoolingLoad,
|
||||||
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
@@ -78,7 +78,7 @@ struct ManualSTests {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.manualS) var manualS
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
let response = try await manualS.coolingInterpolation(.init(
|
let response = try await manualS.interpolate.cooling(.init(
|
||||||
summerDesignInfo: .mock,
|
summerDesignInfo: .mock,
|
||||||
coolingLoad: .mockCoolingLoad,
|
coolingLoad: .mockCoolingLoad,
|
||||||
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
@@ -108,7 +108,7 @@ struct ManualSTests {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.manualS) var manualS
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
let response = try await manualS.coolingInterpolation(.init(
|
let response = try await manualS.interpolate.cooling(.init(
|
||||||
summerDesignInfo: .mock,
|
summerDesignInfo: .mock,
|
||||||
coolingLoad: .mockCoolingLoad,
|
coolingLoad: .mockCoolingLoad,
|
||||||
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
@@ -145,7 +145,7 @@ struct ManualSTests {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.manualS) var manualS
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
let response = try await manualS.heatPumpHeatingInterpolation(.init(
|
let response = try await manualS.interpolate.heatPumpHeating(.init(
|
||||||
winterDesignTemperature: 5,
|
winterDesignTemperature: 5,
|
||||||
heatLoss: 49667,
|
heatLoss: 49667,
|
||||||
climateType: .mildWinterOrLatentLoad,
|
climateType: .mildWinterOrLatentLoad,
|
||||||
@@ -166,7 +166,7 @@ struct ManualSTests {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.manualS) var manualS
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
let response = try await manualS.furnaceInterpolation(
|
let response = try await manualS.interpolate.furnace(
|
||||||
.init(
|
.init(
|
||||||
elevation: nil,
|
elevation: nil,
|
||||||
winterDesignTemperature: 5,
|
winterDesignTemperature: 5,
|
||||||
@@ -189,7 +189,7 @@ struct ManualSTests {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.manualS) var manualS
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
let response = try await manualS.furnaceInterpolation(
|
let response = try await manualS.interpolate.furnace(
|
||||||
.init(
|
.init(
|
||||||
elevation: nil,
|
elevation: nil,
|
||||||
winterDesignTemperature: 5,
|
winterDesignTemperature: 5,
|
||||||
@@ -204,6 +204,46 @@ struct ManualSTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func proposedKWInterpolation() async throws {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.manualS = .liveValue
|
||||||
|
} operation: {
|
||||||
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
|
let response = try await manualS.interpolate.proposeKW(
|
||||||
|
.init(heatLoss: 49667, proposedKW: 15)
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(!response.failed)
|
||||||
|
#expect(response.capacityAsPercentOfLoad == 107)
|
||||||
|
#expect(response.supplementalHeatRequired == 14)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func proposedKWInterpolation_with_heatPump() async throws {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.manualS = .liveValue
|
||||||
|
} operation: {
|
||||||
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
|
let response = try await manualS.interpolate.proposeKW(
|
||||||
|
.init(
|
||||||
|
winterDesignTemperature: 5,
|
||||||
|
climateType: .mildWinterOrLatentLoad,
|
||||||
|
capacity: .mock,
|
||||||
|
heatLoss: 49667,
|
||||||
|
proposedKW: 15
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(!response.failed)
|
||||||
|
#expect(response.capacityAsPercentOfLoad == 136)
|
||||||
|
#expect(response.supplementalHeatRequired == 11)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func balancePoint() async throws {
|
func balancePoint() async throws {
|
||||||
try await withDependencies {
|
try await withDependencies {
|
||||||
|
|||||||
Reference in New Issue
Block a user