|
|
|
|
@@ -1,32 +1,26 @@
|
|
|
|
|
import Models
|
|
|
|
|
import Validations
|
|
|
|
|
|
|
|
|
|
extension Interpolate.Request {
|
|
|
|
|
func respond() async throws -> Interpolate.Response {
|
|
|
|
|
extension CoolingInterpolation.Request {
|
|
|
|
|
|
|
|
|
|
func respond() async throws -> CoolingInterpolation.Response {
|
|
|
|
|
try await validate()
|
|
|
|
|
let interpolatedCapacity = await interpolation.interpolatedCapacity(
|
|
|
|
|
outdoorDesignTemperature: designInfo.summer.outdoorTemperature
|
|
|
|
|
let interpolatedCapacity = await interpolatedCapacity()
|
|
|
|
|
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(
|
|
|
|
|
total: normalizePercentage(finalCapacity.total, houseLoad.coolingTotal),
|
|
|
|
|
sensible: normalizePercentage(finalCapacity.sensible, houseLoad.coolingSensible),
|
|
|
|
|
latent: normalizePercentage(finalCapacity.latent, houseLoad.coolingLatent)
|
|
|
|
|
total: normalizePercentage(finalCapacity.total, coolingLoad.total),
|
|
|
|
|
sensible: normalizePercentage(finalCapacity.sensible, coolingLoad.sensible),
|
|
|
|
|
latent: normalizePercentage(finalCapacity.latent, coolingLoad.latent)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return .init(
|
|
|
|
|
@@ -39,6 +33,57 @@ extension Interpolate.Request {
|
|
|
|
|
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 {
|
|
|
|
|
@@ -92,29 +137,7 @@ private func normalizePercentage(
|
|
|
|
|
return Int((value * 1000).rounded() / 10.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private extension Capacity.Cooling {
|
|
|
|
|
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 {
|
|
|
|
|
private extension CoolingInterpolation.InterpolationRequest {
|
|
|
|
|
|
|
|
|
|
func interpolatedCapacity(outdoorDesignTemperature: Int) async -> Capacity.Cooling {
|
|
|
|
|
switch self {
|
|
|
|
|
@@ -147,20 +170,16 @@ private func interpolateIndoorCapacity(
|
|
|
|
|
above: Capacity.ManufacturersContainer,
|
|
|
|
|
below: Capacity.ManufacturersContainer
|
|
|
|
|
) async -> Capacity.Cooling {
|
|
|
|
|
let total = Double(below.totalCapacity)
|
|
|
|
|
+ (
|
|
|
|
|
Double(above.totalCapacity - below.totalCapacity)
|
|
|
|
|
/ Double(above.wetBulb - below.wetBulb)
|
|
|
|
|
)
|
|
|
|
|
* Double(63 - below.wetBulb)
|
|
|
|
|
let total = below.totalCapacity
|
|
|
|
|
+ ((above.totalCapacity - below.totalCapacity) / (above.wetBulb - below.wetBulb))
|
|
|
|
|
* (63 - below.wetBulb)
|
|
|
|
|
|
|
|
|
|
let sensible = Double(below.sensibleCapacity)
|
|
|
|
|
+ Double(above.sensibleCapacity - below.sensibleCapacity)
|
|
|
|
|
/ Double(below.totalCapacity - above.totalCapacity)
|
|
|
|
|
* Double(below.totalCapacity)
|
|
|
|
|
- total
|
|
|
|
|
+ (Double(above.sensibleCapacity) - Double(below.sensibleCapacity))
|
|
|
|
|
/ (Double(below.totalCapacity) - Double(above.totalCapacity))
|
|
|
|
|
* (Double(below.totalCapacity) - Double(total))
|
|
|
|
|
|
|
|
|
|
return .init(total: Int(total), sensible: Int(sensible))
|
|
|
|
|
return .init(total: total, sensible: Int(sensible))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func interpolateOutdoorCapacity(
|
|
|
|
|
@@ -175,7 +194,7 @@ private func interpolateOutdoorCapacity(
|
|
|
|
|
* ((belowCapacity - aboveCapacity) / (aboveOutdoorTemperature - belowOutdoorTemperature))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private extension Interpolate.OneWayIndoor {
|
|
|
|
|
private extension CoolingInterpolation.OneWayIndoor {
|
|
|
|
|
|
|
|
|
|
func interpolatedCapacity() async -> Capacity.Cooling {
|
|
|
|
|
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 {
|
|
|
|
|
let total = await interpolate(
|
|
|
|
|
outdoorDesignTemperature: outdoorDesignTemperature,
|
|
|
|
|
@@ -203,8 +222,8 @@ private extension Interpolate.OneWayOutdoor {
|
|
|
|
|
|
|
|
|
|
private func interpolate(
|
|
|
|
|
outdoorDesignTemperature: Int,
|
|
|
|
|
aboveCapacity: KeyPath<Interpolate.OneWayOutdoor.Capacities.Capacity, Int>,
|
|
|
|
|
belowCapacity: KeyPath<Interpolate.OneWayOutdoor.Capacities.Capacity, Int>,
|
|
|
|
|
aboveCapacity: KeyPath<CoolingInterpolation.OneWayOutdoor.Capacities.Capacity, Int>,
|
|
|
|
|
belowCapacity: KeyPath<CoolingInterpolation.OneWayOutdoor.Capacities.Capacity, Int>,
|
|
|
|
|
) async -> Int {
|
|
|
|
|
return await interpolateOutdoorCapacity(
|
|
|
|
|
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 {
|
|
|
|
|
let aboveIndoorInterpolation = await self.aboveIndoorInterpolation()
|
|
|
|
|
@@ -233,7 +252,7 @@ private extension Interpolate.TwoWay {
|
|
|
|
|
aboveIndoor: Capacity.Cooling,
|
|
|
|
|
belowIndoor: Capacity.Cooling
|
|
|
|
|
) async -> Capacity.Cooling {
|
|
|
|
|
let request = Interpolate.OneWayOutdoor(
|
|
|
|
|
let request = CoolingInterpolation.OneWayOutdoor(
|
|
|
|
|
airflow: 0,
|
|
|
|
|
wetBulb: 63,
|
|
|
|
|
capacities: .init(
|
|
|
|
|
@@ -271,19 +290,19 @@ private extension Interpolate.TwoWay {
|
|
|
|
|
// MARK: - Validations
|
|
|
|
|
|
|
|
|
|
// Basic validations of the request.
|
|
|
|
|
extension Interpolate.Request: AsyncValidatable {
|
|
|
|
|
extension CoolingInterpolation.Request: AsyncValidatable {
|
|
|
|
|
public var body: some AsyncValidation<Self> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
AsyncValidator.validate(\.designInfo)
|
|
|
|
|
AsyncValidator.validate(\.houseLoad)
|
|
|
|
|
AsyncValidator.validate(\.summerDesignInfo)
|
|
|
|
|
AsyncValidator.validate(\.coolingLoad)
|
|
|
|
|
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 {
|
|
|
|
|
switch value {
|
|
|
|
|
@@ -299,7 +318,7 @@ extension Interpolate.InterpolationRequest: AsyncValidatable {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Interpolate.OneWayIndoor: AsyncValidatable {
|
|
|
|
|
extension CoolingInterpolation.OneWayIndoor: AsyncValidatable {
|
|
|
|
|
public var body: some AsyncValidation<Self> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
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> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
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> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
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> {
|
|
|
|
|
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> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
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> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
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> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
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> {
|
|
|
|
|
AsyncValidator.accumulating {
|
|
|
|
|
AsyncValidator.validate(\.aboveDewPoint)
|