feat: Adds cooling interpolation tests.
This commit is contained in:
@@ -1,6 +1,16 @@
|
|||||||
import Models
|
import Models
|
||||||
import Validations
|
import Validations
|
||||||
|
|
||||||
|
extension Capacity.Cooling: AsyncValidatable {
|
||||||
|
public var body: some AsyncValidation<Self> {
|
||||||
|
AsyncValidator.accumulating {
|
||||||
|
AsyncValidator.greaterThan(\.total, 0)
|
||||||
|
AsyncValidator.greaterThan(\.sensible, 0)
|
||||||
|
AsyncValidator.greaterThanOrEquals(\.total, \.sensible)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Capacity.ManufacturersCooling: AsyncValidatable {
|
extension Capacity.ManufacturersCooling: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
|
|||||||
@@ -1,32 +1,26 @@
|
|||||||
import Models
|
import Models
|
||||||
import Validations
|
import Validations
|
||||||
|
|
||||||
extension Interpolate.Request {
|
extension CoolingInterpolation.Request {
|
||||||
func respond() async throws -> Interpolate.Response {
|
|
||||||
|
func respond() async throws -> CoolingInterpolation.Response {
|
||||||
try await validate()
|
try await validate()
|
||||||
let interpolatedCapacity = await interpolation.interpolatedCapacity(
|
let interpolatedCapacity = await interpolatedCapacity()
|
||||||
outdoorDesignTemperature: designInfo.summer.outdoorTemperature
|
let excessLatent = excessLatent(interpolatedCapacity.latent)
|
||||||
|
let elevationDeratings = try await elevationDeratings()
|
||||||
|
let sizingLimits = try await sizingLimits()
|
||||||
|
|
||||||
|
let finalCapacity = calculateFinalCapacity(
|
||||||
|
interpolated: interpolatedCapacity,
|
||||||
|
excessLatent: excessLatent,
|
||||||
|
adjustmentMultipliers: interpolation.adjustmentMultipliers,
|
||||||
|
elevationDeratings: elevationDeratings
|
||||||
)
|
)
|
||||||
let excessLatent = self.excessLatent(interpolatedLatent: interpolatedCapacity.latent)
|
|
||||||
let elevationDeratings = try await Derating.Request(
|
|
||||||
elevation: designInfo.elevation,
|
|
||||||
systemType: systemType
|
|
||||||
).respond()
|
|
||||||
|
|
||||||
let sizingLimits = try await SizingLimits.Request(
|
|
||||||
systemType: systemType,
|
|
||||||
houseLoad: houseLoad
|
|
||||||
).respond()
|
|
||||||
|
|
||||||
let finalCapacity = interpolatedCapacity
|
|
||||||
.applying(excessLatent: excessLatent)
|
|
||||||
.applying(adjustmentMultipliers: interpolation.adjustmentMultipliers)
|
|
||||||
.applying(adjustmentMultipliers: elevationDeratings)
|
|
||||||
|
|
||||||
let capacityAsPercentOfLoad = Capacity.Cooling(
|
let capacityAsPercentOfLoad = Capacity.Cooling(
|
||||||
total: normalizePercentage(finalCapacity.total, houseLoad.coolingTotal),
|
total: normalizePercentage(finalCapacity.total, coolingLoad.total),
|
||||||
sensible: normalizePercentage(finalCapacity.sensible, houseLoad.coolingSensible),
|
sensible: normalizePercentage(finalCapacity.sensible, coolingLoad.sensible),
|
||||||
latent: normalizePercentage(finalCapacity.latent, houseLoad.coolingLatent)
|
latent: normalizePercentage(finalCapacity.latent, coolingLoad.latent)
|
||||||
)
|
)
|
||||||
|
|
||||||
return .init(
|
return .init(
|
||||||
@@ -39,6 +33,57 @@ extension Interpolate.Request {
|
|||||||
sizingLimits: sizingLimits
|
sizingLimits: sizingLimits
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func interpolatedCapacity() async -> Capacity.Cooling {
|
||||||
|
await interpolation.interpolatedCapacity(
|
||||||
|
outdoorDesignTemperature: summerDesignInfo.outdoorTemperature
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func elevationDeratings() async throws -> AdjustmentMultiplier? {
|
||||||
|
guard let elevation else { return nil }
|
||||||
|
|
||||||
|
return try await Derating.Request(elevation: elevation, systemType: systemType)
|
||||||
|
.respond()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sizingLimits() async throws -> SizingLimits.Response {
|
||||||
|
try await SizingLimits.Request(systemType: systemType, coolingLoad: coolingLoad)
|
||||||
|
.respond()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func excessLatent(_ interpolatedLatent: Int) -> Int {
|
||||||
|
(interpolatedLatent - coolingLoad.latent) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func calculateFinalCapacity(
|
||||||
|
interpolated: Capacity.Cooling,
|
||||||
|
excessLatent: Int,
|
||||||
|
adjustmentMultipliers: AdjustmentMultiplier?,
|
||||||
|
elevationDeratings: AdjustmentMultiplier?
|
||||||
|
) -> Capacity.Cooling {
|
||||||
|
var total = interpolated.total
|
||||||
|
var sensible = interpolated.sensible + excessLatent
|
||||||
|
if let adjustmentMultipliers {
|
||||||
|
adjustmentMultipliers.apply(total: &total, sensible: &sensible)
|
||||||
|
}
|
||||||
|
if let elevationDeratings {
|
||||||
|
elevationDeratings.apply(total: &total, sensible: &sensible)
|
||||||
|
}
|
||||||
|
return .init(total: total, sensible: sensible)
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AdjustmentMultiplier {
|
||||||
|
func apply(total: inout Int, sensible: inout Int) {
|
||||||
|
if let coolingTotal {
|
||||||
|
total = Int(Double(total) * coolingTotal)
|
||||||
|
}
|
||||||
|
if let coolingSensible {
|
||||||
|
sensible = Int(Double(sensible) * coolingSensible)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension SizingLimits.Response {
|
private extension SizingLimits.Response {
|
||||||
@@ -92,29 +137,7 @@ private func normalizePercentage(
|
|||||||
return Int((value * 1000).rounded() / 10.0)
|
return Int((value * 1000).rounded() / 10.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Capacity.Cooling {
|
private extension CoolingInterpolation.InterpolationRequest {
|
||||||
func applying(excessLatent: Int) -> Self {
|
|
||||||
.init(total: total, sensible: sensible + excessLatent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func applying(adjustmentMultipliers: AdjustmentMultiplier?) -> Self {
|
|
||||||
guard let adjustmentMultipliers else { return self }
|
|
||||||
|
|
||||||
return .init(
|
|
||||||
total: Int(Double(total) * (adjustmentMultipliers.coolingTotal ?? 1)),
|
|
||||||
sensible: Int(Double(sensible) * (adjustmentMultipliers.coolingSensible ?? 1))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension Interpolate.Request {
|
|
||||||
|
|
||||||
func excessLatent(interpolatedLatent: Int) -> Int {
|
|
||||||
(interpolatedLatent - houseLoad.coolingLatent) / 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension Interpolate.InterpolationRequest {
|
|
||||||
|
|
||||||
func interpolatedCapacity(outdoorDesignTemperature: Int) async -> Capacity.Cooling {
|
func interpolatedCapacity(outdoorDesignTemperature: Int) async -> Capacity.Cooling {
|
||||||
switch self {
|
switch self {
|
||||||
@@ -147,20 +170,16 @@ private func interpolateIndoorCapacity(
|
|||||||
above: Capacity.ManufacturersContainer,
|
above: Capacity.ManufacturersContainer,
|
||||||
below: Capacity.ManufacturersContainer
|
below: Capacity.ManufacturersContainer
|
||||||
) async -> Capacity.Cooling {
|
) async -> Capacity.Cooling {
|
||||||
let total = Double(below.totalCapacity)
|
let total = below.totalCapacity
|
||||||
+ (
|
+ ((above.totalCapacity - below.totalCapacity) / (above.wetBulb - below.wetBulb))
|
||||||
Double(above.totalCapacity - below.totalCapacity)
|
* (63 - below.wetBulb)
|
||||||
/ Double(above.wetBulb - below.wetBulb)
|
|
||||||
)
|
|
||||||
* Double(63 - below.wetBulb)
|
|
||||||
|
|
||||||
let sensible = Double(below.sensibleCapacity)
|
let sensible = Double(below.sensibleCapacity)
|
||||||
+ Double(above.sensibleCapacity - below.sensibleCapacity)
|
+ (Double(above.sensibleCapacity) - Double(below.sensibleCapacity))
|
||||||
/ Double(below.totalCapacity - above.totalCapacity)
|
/ (Double(below.totalCapacity) - Double(above.totalCapacity))
|
||||||
* Double(below.totalCapacity)
|
* (Double(below.totalCapacity) - Double(total))
|
||||||
- total
|
|
||||||
|
|
||||||
return .init(total: Int(total), sensible: Int(sensible))
|
return .init(total: total, sensible: Int(sensible))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func interpolateOutdoorCapacity(
|
private func interpolateOutdoorCapacity(
|
||||||
@@ -175,7 +194,7 @@ private func interpolateOutdoorCapacity(
|
|||||||
* ((belowCapacity - aboveCapacity) / (aboveOutdoorTemperature - belowOutdoorTemperature))
|
* ((belowCapacity - aboveCapacity) / (aboveOutdoorTemperature - belowOutdoorTemperature))
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Interpolate.OneWayIndoor {
|
private extension CoolingInterpolation.OneWayIndoor {
|
||||||
|
|
||||||
func interpolatedCapacity() async -> Capacity.Cooling {
|
func interpolatedCapacity() async -> Capacity.Cooling {
|
||||||
return await interpolateIndoorCapacity(
|
return await interpolateIndoorCapacity(
|
||||||
@@ -185,7 +204,7 @@ private extension Interpolate.OneWayIndoor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Interpolate.OneWayOutdoor {
|
private extension CoolingInterpolation.OneWayOutdoor {
|
||||||
func interpolatedCapacity(outdoorDesignTemperature: Int) async -> Capacity.Cooling {
|
func interpolatedCapacity(outdoorDesignTemperature: Int) async -> Capacity.Cooling {
|
||||||
let total = await interpolate(
|
let total = await interpolate(
|
||||||
outdoorDesignTemperature: outdoorDesignTemperature,
|
outdoorDesignTemperature: outdoorDesignTemperature,
|
||||||
@@ -203,8 +222,8 @@ private extension Interpolate.OneWayOutdoor {
|
|||||||
|
|
||||||
private func interpolate(
|
private func interpolate(
|
||||||
outdoorDesignTemperature: Int,
|
outdoorDesignTemperature: Int,
|
||||||
aboveCapacity: KeyPath<Interpolate.OneWayOutdoor.Capacities.Capacity, Int>,
|
aboveCapacity: KeyPath<CoolingInterpolation.OneWayOutdoor.Capacities.Capacity, Int>,
|
||||||
belowCapacity: KeyPath<Interpolate.OneWayOutdoor.Capacities.Capacity, Int>,
|
belowCapacity: KeyPath<CoolingInterpolation.OneWayOutdoor.Capacities.Capacity, Int>,
|
||||||
) async -> Int {
|
) async -> Int {
|
||||||
return await interpolateOutdoorCapacity(
|
return await interpolateOutdoorCapacity(
|
||||||
outdoorDesignTemperature: outdoorDesignTemperature,
|
outdoorDesignTemperature: outdoorDesignTemperature,
|
||||||
@@ -216,7 +235,7 @@ private extension Interpolate.OneWayOutdoor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Interpolate.TwoWay {
|
private extension CoolingInterpolation.TwoWay {
|
||||||
|
|
||||||
func interpolatedCapacity(outdoorDesignTemperature: Int) async -> Capacity.Cooling {
|
func interpolatedCapacity(outdoorDesignTemperature: Int) async -> Capacity.Cooling {
|
||||||
let aboveIndoorInterpolation = await self.aboveIndoorInterpolation()
|
let aboveIndoorInterpolation = await self.aboveIndoorInterpolation()
|
||||||
@@ -233,7 +252,7 @@ private extension Interpolate.TwoWay {
|
|||||||
aboveIndoor: Capacity.Cooling,
|
aboveIndoor: Capacity.Cooling,
|
||||||
belowIndoor: Capacity.Cooling
|
belowIndoor: Capacity.Cooling
|
||||||
) async -> Capacity.Cooling {
|
) async -> Capacity.Cooling {
|
||||||
let request = Interpolate.OneWayOutdoor(
|
let request = CoolingInterpolation.OneWayOutdoor(
|
||||||
airflow: 0,
|
airflow: 0,
|
||||||
wetBulb: 63,
|
wetBulb: 63,
|
||||||
capacities: .init(
|
capacities: .init(
|
||||||
@@ -271,19 +290,19 @@ private extension Interpolate.TwoWay {
|
|||||||
// MARK: - Validations
|
// MARK: - Validations
|
||||||
|
|
||||||
// Basic validations of the request.
|
// Basic validations of the request.
|
||||||
extension Interpolate.Request: AsyncValidatable {
|
extension CoolingInterpolation.Request: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.validate(\.designInfo)
|
AsyncValidator.validate(\.summerDesignInfo)
|
||||||
AsyncValidator.validate(\.houseLoad)
|
AsyncValidator.validate(\.coolingLoad)
|
||||||
AsyncValidator.validate(\.interpolation)
|
AsyncValidator.validate(\.interpolation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.InterpolationRequest: AsyncValidatable {
|
extension CoolingInterpolation.InterpolationRequest: AsyncValidatable {
|
||||||
|
|
||||||
public typealias Value = Interpolate.InterpolationRequest
|
public typealias Value = CoolingInterpolation.InterpolationRequest
|
||||||
|
|
||||||
public func validate(_ value: Self) async throws {
|
public func validate(_ value: Self) async throws {
|
||||||
switch value {
|
switch value {
|
||||||
@@ -299,7 +318,7 @@ extension Interpolate.InterpolationRequest: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.OneWayIndoor: AsyncValidatable {
|
extension CoolingInterpolation.OneWayIndoor: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.greaterThan(\.airflow, 0)
|
AsyncValidator.greaterThan(\.airflow, 0)
|
||||||
@@ -309,7 +328,7 @@ extension Interpolate.OneWayIndoor: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.OneWayIndoor.Capacities: AsyncValidatable {
|
extension CoolingInterpolation.OneWayIndoor.Capacities: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.validate(\.aboveDewpoint)
|
AsyncValidator.validate(\.aboveDewpoint)
|
||||||
@@ -318,7 +337,7 @@ extension Interpolate.OneWayIndoor.Capacities: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.OneWayOutdoor: AsyncValidatable {
|
extension CoolingInterpolation.OneWayOutdoor: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.greaterThan(\.airflow, 0)
|
AsyncValidator.greaterThan(\.airflow, 0)
|
||||||
@@ -328,7 +347,7 @@ extension Interpolate.OneWayOutdoor: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.OneWayOutdoor.Capacities: AsyncValidatable {
|
extension CoolingInterpolation.OneWayOutdoor.Capacities: AsyncValidatable {
|
||||||
|
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
@@ -338,7 +357,7 @@ extension Interpolate.OneWayOutdoor.Capacities: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.OneWayOutdoor.Capacities.Capacity: AsyncValidatable {
|
extension CoolingInterpolation.OneWayOutdoor.Capacities.Capacity: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.greaterThan(\.outdoorTemperature, 0)
|
AsyncValidator.greaterThan(\.outdoorTemperature, 0)
|
||||||
@@ -349,7 +368,7 @@ extension Interpolate.OneWayOutdoor.Capacities.Capacity: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.TwoWay: AsyncValidatable {
|
extension CoolingInterpolation.TwoWay: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.greaterThan(\.airflow, 0)
|
AsyncValidator.greaterThan(\.airflow, 0)
|
||||||
@@ -358,7 +377,7 @@ extension Interpolate.TwoWay: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.TwoWay.Capacities: AsyncValidatable {
|
extension CoolingInterpolation.TwoWay.Capacities: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.validate(\.above)
|
AsyncValidator.validate(\.above)
|
||||||
@@ -367,7 +386,7 @@ extension Interpolate.TwoWay.Capacities: AsyncValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Interpolate.TwoWay.Capacities.CapacityContainer: AsyncValidatable {
|
extension CoolingInterpolation.TwoWay.Capacities.CapacityContainer: AsyncValidatable {
|
||||||
public var body: some AsyncValidation<Self> {
|
public var body: some AsyncValidation<Self> {
|
||||||
AsyncValidator.accumulating {
|
AsyncValidator.accumulating {
|
||||||
AsyncValidator.validate(\.aboveDewPoint)
|
AsyncValidator.validate(\.aboveDewPoint)
|
||||||
@@ -4,7 +4,7 @@ import Models
|
|||||||
extension SizingLimits.Request {
|
extension SizingLimits.Request {
|
||||||
func respond() async throws -> SizingLimits.Response {
|
func respond() async throws -> SizingLimits.Response {
|
||||||
return try .init(
|
return try .init(
|
||||||
oversizing: .oversizingLimit(systemType: systemType, houseLoad: houseLoad),
|
oversizing: .oversizingLimit(systemType: systemType, houseLoad: coolingLoad),
|
||||||
undersizing: .undersizingLimits
|
undersizing: .undersizingLimits
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ private extension SizingLimits.Limits {
|
|||||||
|
|
||||||
static func oversizingLimit(
|
static func oversizingLimit(
|
||||||
systemType: SystemType,
|
systemType: SystemType,
|
||||||
houseLoad: HouseLoad?
|
houseLoad: Capacity.Cooling?
|
||||||
) throws -> Self {
|
) throws -> Self {
|
||||||
switch systemType {
|
switch systemType {
|
||||||
case let .heatingOnly(type: type):
|
case let .heatingOnly(type: type):
|
||||||
@@ -51,7 +51,7 @@ private extension SystemType.HeatingOnlyType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func coolingTotalOversizingLimit(
|
private func coolingTotalOversizingLimit(
|
||||||
houseLoad: HouseLoad?,
|
houseLoad: Capacity.Cooling?,
|
||||||
compressorType: SystemType.CompressorType,
|
compressorType: SystemType.CompressorType,
|
||||||
climateType: SystemType.ClimateType
|
climateType: SystemType.ClimateType
|
||||||
) throws -> Int {
|
) throws -> Int {
|
||||||
@@ -67,7 +67,7 @@ private func coolingTotalOversizingLimit(
|
|||||||
guard let houseLoad else {
|
guard let houseLoad else {
|
||||||
throw HouseLoadError()
|
throw HouseLoadError()
|
||||||
}
|
}
|
||||||
let decimal = Double(houseLoad.coolingTotal + 15000) / Double(houseLoad.coolingTotal)
|
let decimal = Double(houseLoad.total + 15000) / Double(houseLoad.total)
|
||||||
return Int(round(decimal * 100))
|
return Int(round(decimal * 100))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public extension DependencyValues {
|
|||||||
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: @Sendable (Interpolate.Request) async throws -> Interpolate.Response
|
public var coolingInterpolation: @Sendable (CoolingInterpolation.Request) async throws -> CoolingInterpolation.Response
|
||||||
public var requiredKW: @Sendable (RequiredKW.Request) async throws -> RequiredKW.Response
|
public var requiredKW: @Sendable (RequiredKW.Request) async throws -> RequiredKW.Response
|
||||||
public var sizingLimits: @Sendable (SizingLimits.Request) async throws -> SizingLimits.Response
|
public var sizingLimits: @Sendable (SizingLimits.Request) async throws -> SizingLimits.Response
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ 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() },
|
||||||
interpolate: { try await $0.respond() },
|
coolingInterpolation: { try await $0.respond() },
|
||||||
requiredKW: { try await $0.respond() },
|
requiredKW: { try await $0.respond() },
|
||||||
sizingLimits: { try await $0.respond() }
|
sizingLimits: { try await $0.respond() }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
public enum Interpolate {
|
public enum CoolingInterpolation {
|
||||||
|
|
||||||
public struct Request: Codable, Equatable, Sendable {
|
public struct Request: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
public let designInfo: DesignInfo
|
public let elevation: Int?
|
||||||
public let houseLoad: HouseLoad
|
public let summerDesignInfo: DesignInfo.Summer
|
||||||
|
public let coolingLoad: Models.Capacity.Cooling
|
||||||
public let systemType: SystemType
|
public let systemType: SystemType
|
||||||
public let interpolation: InterpolationRequest
|
public let interpolation: InterpolationRequest
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
designInfo: DesignInfo,
|
elevation: Int? = nil,
|
||||||
houseLoad: HouseLoad,
|
summerDesignInfo: DesignInfo.Summer,
|
||||||
|
coolingLoad: Capacity.Cooling,
|
||||||
systemType: SystemType,
|
systemType: SystemType,
|
||||||
interpolation: InterpolationRequest
|
interpolation: InterpolationRequest
|
||||||
) {
|
) {
|
||||||
self.designInfo = designInfo
|
self.elevation = elevation
|
||||||
self.houseLoad = houseLoad
|
self.summerDesignInfo = summerDesignInfo
|
||||||
|
self.coolingLoad = coolingLoad
|
||||||
self.systemType = systemType
|
self.systemType = systemType
|
||||||
self.interpolation = interpolation
|
self.interpolation = interpolation
|
||||||
}
|
}
|
||||||
@@ -24,18 +27,18 @@ public enum Interpolate {
|
|||||||
|
|
||||||
public let failed: Bool
|
public let failed: Bool
|
||||||
public let failures: [String]?
|
public let failures: [String]?
|
||||||
public let interpolatedCapacity: Capacity.Cooling
|
public let interpolatedCapacity: Models.Capacity.Cooling
|
||||||
public let excessLatent: Int
|
public let excessLatent: Int
|
||||||
public let finalCapacityAtDesign: Capacity.Cooling
|
public let finalCapacityAtDesign: Models.Capacity.Cooling
|
||||||
public let altitudeDerating: AdjustmentMultiplier?
|
public let altitudeDerating: AdjustmentMultiplier?
|
||||||
public let capacityAsPercentOfLoad: Capacity.Cooling
|
public let capacityAsPercentOfLoad: Models.Capacity.Cooling
|
||||||
public let sizingLimits: SizingLimits.Response
|
public let sizingLimits: SizingLimits.Response
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
failures: [String]? = nil,
|
failures: [String]? = nil,
|
||||||
interpolatedCapacity: Capacity.Cooling,
|
interpolatedCapacity: Models.Capacity.Cooling,
|
||||||
excessLatent: Int,
|
excessLatent: Int,
|
||||||
finalCapacityAtDesign: Capacity.Cooling,
|
finalCapacityAtDesign: Models.Capacity.Cooling,
|
||||||
altitudeDerating: AdjustmentMultiplier? = nil,
|
altitudeDerating: AdjustmentMultiplier? = nil,
|
||||||
capacityAsPercentOfLoad: Capacity.Cooling,
|
capacityAsPercentOfLoad: Capacity.Cooling,
|
||||||
sizingLimits: SizingLimits.Response
|
sizingLimits: SizingLimits.Response
|
||||||
@@ -52,7 +55,7 @@ public enum Interpolate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum InterpolationRequest: Codable, Equatable, Sendable {
|
public enum InterpolationRequest: Codable, Equatable, Sendable {
|
||||||
case noInterpolation(Capacity.ManufacturersCooling, AdjustmentMultiplier? = nil)
|
case noInterpolation(Models.Capacity.ManufacturersCooling, AdjustmentMultiplier? = nil)
|
||||||
case oneWayIndoor(OneWayIndoor)
|
case oneWayIndoor(OneWayIndoor)
|
||||||
case oneWayOutdoor(OneWayOutdoor)
|
case oneWayOutdoor(OneWayOutdoor)
|
||||||
case twoWay(TwoWay)
|
case twoWay(TwoWay)
|
||||||
@@ -68,7 +71,7 @@ public enum Interpolate {
|
|||||||
public init(
|
public init(
|
||||||
airflow: Int,
|
airflow: Int,
|
||||||
outdoorTemperature: Int,
|
outdoorTemperature: Int,
|
||||||
capacities: Interpolate.OneWayIndoor.Capacities,
|
capacities: CoolingInterpolation.OneWayIndoor.Capacities,
|
||||||
adjustmentMultipliers: AdjustmentMultiplier? = nil
|
adjustmentMultipliers: AdjustmentMultiplier? = nil
|
||||||
) {
|
) {
|
||||||
self.airflow = airflow
|
self.airflow = airflow
|
||||||
@@ -79,10 +82,13 @@ public enum Interpolate {
|
|||||||
|
|
||||||
public struct Capacities: Codable, Equatable, Sendable {
|
public struct Capacities: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
public let aboveDewpoint: Capacity.ManufacturersContainer
|
public let aboveDewpoint: Models.Capacity.ManufacturersContainer
|
||||||
public let belowDewpoint: Capacity.ManufacturersContainer
|
public let belowDewpoint: Models.Capacity.ManufacturersContainer
|
||||||
|
|
||||||
public init(aboveDewpoint: Capacity.ManufacturersContainer, belowDewpoint: Capacity.ManufacturersContainer) {
|
public init(
|
||||||
|
aboveDewpoint: Models.Capacity.ManufacturersContainer,
|
||||||
|
belowDewpoint: Models.Capacity.ManufacturersContainer
|
||||||
|
) {
|
||||||
self.aboveDewpoint = aboveDewpoint
|
self.aboveDewpoint = aboveDewpoint
|
||||||
self.belowDewpoint = belowDewpoint
|
self.belowDewpoint = belowDewpoint
|
||||||
}
|
}
|
||||||
@@ -99,7 +105,7 @@ public enum Interpolate {
|
|||||||
public init(
|
public init(
|
||||||
airflow: Int,
|
airflow: Int,
|
||||||
wetBulb: Int,
|
wetBulb: Int,
|
||||||
capacities: Interpolate.OneWayOutdoor.Capacities,
|
capacities: CoolingInterpolation.OneWayOutdoor.Capacities,
|
||||||
adjustmentMultipliers: AdjustmentMultiplier? = nil
|
adjustmentMultipliers: AdjustmentMultiplier? = nil
|
||||||
) {
|
) {
|
||||||
self.airflow = airflow
|
self.airflow = airflow
|
||||||
@@ -114,8 +120,8 @@ public enum Interpolate {
|
|||||||
public let belowOutdoor: Capacity
|
public let belowOutdoor: Capacity
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
aboveOutdoor: Interpolate.OneWayOutdoor.Capacities.Capacity,
|
aboveOutdoor: CoolingInterpolation.OneWayOutdoor.Capacities.Capacity,
|
||||||
belowOutdoor: Interpolate.OneWayOutdoor.Capacities.Capacity
|
belowOutdoor: CoolingInterpolation.OneWayOutdoor.Capacities.Capacity
|
||||||
) {
|
) {
|
||||||
self.aboveOutdoor = aboveOutdoor
|
self.aboveOutdoor = aboveOutdoor
|
||||||
self.belowOutdoor = belowOutdoor
|
self.belowOutdoor = belowOutdoor
|
||||||
@@ -144,7 +150,7 @@ public enum Interpolate {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
airflow: Int,
|
airflow: Int,
|
||||||
capacities: Interpolate.TwoWay.Capacities,
|
capacities: CoolingInterpolation.TwoWay.Capacities,
|
||||||
adjustmentMultipliers: AdjustmentMultiplier? = nil
|
adjustmentMultipliers: AdjustmentMultiplier? = nil
|
||||||
) {
|
) {
|
||||||
self.airflow = airflow
|
self.airflow = airflow
|
||||||
@@ -158,8 +164,8 @@ public enum Interpolate {
|
|||||||
public let below: CapacityContainer
|
public let below: CapacityContainer
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
above: Interpolate.TwoWay.Capacities.CapacityContainer,
|
above: CoolingInterpolation.TwoWay.Capacities.CapacityContainer,
|
||||||
below: Interpolate.TwoWay.Capacities.CapacityContainer
|
below: CoolingInterpolation.TwoWay.Capacities.CapacityContainer
|
||||||
) {
|
) {
|
||||||
self.above = above
|
self.above = above
|
||||||
self.below = below
|
self.below = below
|
||||||
@@ -168,13 +174,13 @@ public enum Interpolate {
|
|||||||
public struct CapacityContainer: Codable, Equatable, Sendable {
|
public struct CapacityContainer: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
public let outdoorTemperature: Int
|
public let outdoorTemperature: Int
|
||||||
public let aboveDewPoint: Capacity.ManufacturersContainer
|
public let aboveDewPoint: Models.Capacity.ManufacturersContainer
|
||||||
public let belowDewPoint: Capacity.ManufacturersContainer
|
public let belowDewPoint: Models.Capacity.ManufacturersContainer
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
outdoorTemperature: Int,
|
outdoorTemperature: Int,
|
||||||
aboveDewPoint: Capacity.ManufacturersContainer,
|
aboveDewPoint: Models.Capacity.ManufacturersContainer,
|
||||||
belowDewPoint: Capacity.ManufacturersContainer
|
belowDewPoint: Models.Capacity.ManufacturersContainer
|
||||||
) {
|
) {
|
||||||
self.outdoorTemperature = outdoorTemperature
|
self.outdoorTemperature = outdoorTemperature
|
||||||
self.aboveDewPoint = aboveDewPoint
|
self.aboveDewPoint = aboveDewPoint
|
||||||
@@ -3,11 +3,11 @@ public enum SizingLimits {
|
|||||||
public struct Request: Codable, Equatable, Sendable {
|
public struct Request: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
public let systemType: SystemType
|
public let systemType: SystemType
|
||||||
public let houseLoad: HouseLoad?
|
public let coolingLoad: Capacity.Cooling?
|
||||||
|
|
||||||
public init(systemType: SystemType, houseLoad: HouseLoad? = nil) {
|
public init(systemType: SystemType, coolingLoad: Capacity.Cooling? = nil) {
|
||||||
self.systemType = systemType
|
self.systemType = systemType
|
||||||
self.houseLoad = houseLoad
|
self.coolingLoad = coolingLoad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,138 @@ import Testing
|
|||||||
@Suite("ManualSTests")
|
@Suite("ManualSTests")
|
||||||
struct ManualSTests {
|
struct ManualSTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func coolingInterpolation_noInterpolation() async throws {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.manualS = .liveValue
|
||||||
|
} operation: {
|
||||||
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
|
let response = try await manualS.coolingInterpolation(.init(
|
||||||
|
summerDesignInfo: .mock,
|
||||||
|
coolingLoad: .mockCoolingLoad,
|
||||||
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
|
interpolation: .noInterpolation(
|
||||||
|
.init(
|
||||||
|
airflow: 800,
|
||||||
|
capacity: .init(
|
||||||
|
dryBulbTemperature: 75,
|
||||||
|
wetBulbTemperature: 63,
|
||||||
|
outdoorTemperature: 90,
|
||||||
|
totalCapacity: 22600,
|
||||||
|
sensibleCapacity: 16850
|
||||||
|
)
|
||||||
|
),
|
||||||
|
nil
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
#expect(response.failed)
|
||||||
|
#expect(response.failures == ["Oversizing total failure."])
|
||||||
|
#expect(response.interpolatedCapacity == .init(total: 22600, sensible: 16850))
|
||||||
|
#expect(response.excessLatent == 886)
|
||||||
|
#expect(response.finalCapacityAtDesign == .init(total: 22600, sensible: 17736))
|
||||||
|
#expect(response.capacityAsPercentOfLoad == .init(total: 126, sensible: 127, latent: 122))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func coolingInterpolation_oneWayIndoor() async throws {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.manualS = .liveValue
|
||||||
|
} operation: {
|
||||||
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
|
let response = try await manualS.coolingInterpolation(.init(
|
||||||
|
summerDesignInfo: .mock,
|
||||||
|
coolingLoad: .mockCoolingLoad,
|
||||||
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
|
interpolation: .oneWayIndoor(.init(
|
||||||
|
airflow: 800,
|
||||||
|
outdoorTemperature: 95,
|
||||||
|
capacities: .init(
|
||||||
|
aboveDewpoint: .init(wetBulb: 67, totalCapacity: 24828, sensibleCapacity: 15937),
|
||||||
|
belowDewpoint: .init(wetBulb: 62, totalCapacity: 23046, sensibleCapacity: 19078)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
))
|
||||||
|
|
||||||
|
#expect(response.failed)
|
||||||
|
#expect(response.failures == ["Oversizing total failure."])
|
||||||
|
#expect(response.interpolatedCapacity == .init(total: 23402, sensible: 18450))
|
||||||
|
#expect(response.excessLatent == 487)
|
||||||
|
#expect(response.finalCapacityAtDesign == .init(total: 23402, sensible: 18937))
|
||||||
|
#expect(response.capacityAsPercentOfLoad == .init(total: 130, sensible: 136, latent: 112))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func coolingInterpolation_oneWayOutdoor() async throws {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.manualS = .liveValue
|
||||||
|
} operation: {
|
||||||
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
|
let response = try await manualS.coolingInterpolation(.init(
|
||||||
|
summerDesignInfo: .mock,
|
||||||
|
coolingLoad: .mockCoolingLoad,
|
||||||
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
|
interpolation: .oneWayOutdoor(.init(
|
||||||
|
airflow: 800,
|
||||||
|
wetBulb: 63,
|
||||||
|
capacities: .init(
|
||||||
|
aboveOutdoor: .init(outdoorTemperature: 95, totalCapacity: 22000, sensibleCapacity: 16600),
|
||||||
|
belowOutdoor: .init(outdoorTemperature: 85, totalCapacity: 23200, sensibleCapacity: 17100)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
))
|
||||||
|
|
||||||
|
#expect(response.failed)
|
||||||
|
#expect(response.failures == ["Oversizing total failure."])
|
||||||
|
#expect(response.interpolatedCapacity == .init(total: 22600, sensible: 16850))
|
||||||
|
#expect(response.excessLatent == 886)
|
||||||
|
#expect(response.finalCapacityAtDesign == .init(total: 22600, sensible: 17736))
|
||||||
|
#expect(response.capacityAsPercentOfLoad == .init(total: 126, sensible: 127, latent: 122))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func coolingInterpolation_twoWay() async throws {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.manualS = .liveValue
|
||||||
|
} operation: {
|
||||||
|
@Dependency(\.manualS) var manualS
|
||||||
|
|
||||||
|
let response = try await manualS.coolingInterpolation(.init(
|
||||||
|
summerDesignInfo: .mock,
|
||||||
|
coolingLoad: .mockCoolingLoad,
|
||||||
|
systemType: .airToAir(type: .airConditioner, compressor: .singleSpeed, climate: .mildWinterOrLatentLoad),
|
||||||
|
interpolation: .twoWay(.init(
|
||||||
|
airflow: 800,
|
||||||
|
capacities: .init(
|
||||||
|
above: .init(
|
||||||
|
outdoorTemperature: 95,
|
||||||
|
aboveDewPoint: .init(wetBulb: 67, totalCapacity: 24828, sensibleCapacity: 15937),
|
||||||
|
belowDewPoint: .init(wetBulb: 62, totalCapacity: 23046, sensibleCapacity: 19078)
|
||||||
|
),
|
||||||
|
below: .init(
|
||||||
|
outdoorTemperature: 85,
|
||||||
|
aboveDewPoint: .init(wetBulb: 67, totalCapacity: 25986, sensibleCapacity: 16330),
|
||||||
|
belowDewPoint: .init(wetBulb: 62, totalCapacity: 24029, sensibleCapacity: 19605)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
))
|
||||||
|
|
||||||
|
#expect(response.failed)
|
||||||
|
#expect(response.failures == ["Oversizing total failure."])
|
||||||
|
#expect(response.interpolatedCapacity == .init(total: 23915, sensible: 18700))
|
||||||
|
#expect(response.excessLatent == 618)
|
||||||
|
#expect(response.finalCapacityAtDesign == .init(total: 23915, sensible: 19318))
|
||||||
|
#expect(response.capacityAsPercentOfLoad == .init(total: 133, sensible: 139, latent: 115))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func balancePoint() async throws {
|
func balancePoint() async throws {
|
||||||
try await withDependencies {
|
try await withDependencies {
|
||||||
@@ -52,7 +184,7 @@ struct ManualSTests {
|
|||||||
|
|
||||||
let limits = try await manualS.sizingLimits(.init(
|
let limits = try await manualS.sizingLimits(.init(
|
||||||
systemType: system,
|
systemType: system,
|
||||||
houseLoad: .init(coolingTotal: 17872, coolingSensible: 13894, heating: 49667)
|
coolingLoad: .init(total: 17872, sensible: 13894)
|
||||||
))
|
))
|
||||||
|
|
||||||
#expect(limits.oversizing.coolingLatent == 150)
|
#expect(limits.oversizing.coolingLatent == 150)
|
||||||
@@ -211,3 +343,11 @@ extension SystemType.HeatingOnlyType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Capacity.Cooling {
|
||||||
|
static let mockCoolingLoad = Self(total: 17872, sensible: 13894)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DesignInfo.Summer {
|
||||||
|
static let mock = Self(outdoorTemperature: 90, indoorTemperature: 75, indoorHumidity: 50)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user