feat: Adds initial room pressure calculations.
This commit is contained in:
@@ -38,7 +38,116 @@ public extension RoomPressure.Request {
|
||||
// ])
|
||||
|
||||
func respond(logger: Logger) async throws -> RoomPressure.Response {
|
||||
fatalError()
|
||||
switch self {
|
||||
case let .knownAirflow(request):
|
||||
return try await calculateKnownAirflow(request, logger)
|
||||
case let .measuredPressure(request):
|
||||
return try await calculateMeasuredPressure(request, logger)
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateKnownAirflow(
|
||||
_ request: RoomPressure.Request.KnownAirflow,
|
||||
_ logger: Logger
|
||||
) async throws -> RoomPressure.Response {
|
||||
try request.validate()
|
||||
|
||||
let totalLeakageArea = calculateDoorLeakageArea(
|
||||
doorWidth: request.doorWidth,
|
||||
doorHeight: request.doorHeight,
|
||||
doorUndercut: request.doorUndercut
|
||||
)
|
||||
|
||||
// Net free area (in^2)
|
||||
let netFreeArea = (request.supplyAirflow / Self.targetVelocity) * 144.0
|
||||
let grilleDimensions = try getStandardGrilleSize(
|
||||
requiredArea: netFreeArea,
|
||||
selectedHeight: request.preferredGrilleHeight.rawValue
|
||||
)
|
||||
let (standardDuctSize, actualVelocity) = calculateDuctMetrics(for: request.supplyAirflow)
|
||||
|
||||
return .init(
|
||||
grilleSize: .init(width: grilleDimensions.width, height: grilleDimensions.height, area: netFreeArea / 144),
|
||||
ductSize: .init(diameter: standardDuctSize, velocity: actualVelocity),
|
||||
warnings: generateWarnings(
|
||||
roomPressure: request.targetRoomPressure,
|
||||
actualVelocity: actualVelocity,
|
||||
totalLeakageArea: totalLeakageArea,
|
||||
netFreeArea: netFreeArea
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private func calculateMeasuredPressure(
|
||||
_ request: RoomPressure.Request.MeasuredPressure,
|
||||
_ logger: Logger
|
||||
) async throws -> RoomPressure.Response {
|
||||
try request.validate()
|
||||
|
||||
let totalLeakageArea = calculateDoorLeakageArea(
|
||||
doorWidth: request.doorWidth,
|
||||
doorHeight: request.doorHeight,
|
||||
doorUndercut: request.doorUndercut
|
||||
)
|
||||
|
||||
let pressureInchesWaterColumn = request.measuredRoomPressure * Self.pascalToIWCMultiplier
|
||||
let calculatedAirflow = totalLeakageArea * 4005 * sqrt(pressureInchesWaterColumn)
|
||||
// in^2
|
||||
let netFreeArea = (calculatedAirflow / Self.targetVelocity) * 144
|
||||
let grilleDimensions = try getStandardGrilleSize(
|
||||
requiredArea: netFreeArea,
|
||||
selectedHeight: request.preferredGrilleHeight.rawValue
|
||||
)
|
||||
let (standardDuctSize, actualVelocity) = calculateDuctMetrics(for: calculatedAirflow)
|
||||
|
||||
return .init(
|
||||
grilleSize: .init(width: grilleDimensions.width, height: grilleDimensions.height, area: netFreeArea / 144),
|
||||
ductSize: .init(diameter: standardDuctSize, velocity: actualVelocity),
|
||||
calculatedAirflow: calculatedAirflow,
|
||||
warnings: generateWarnings(
|
||||
roomPressure: request.measuredRoomPressure,
|
||||
actualVelocity: actualVelocity,
|
||||
totalLeakageArea: totalLeakageArea,
|
||||
netFreeArea: netFreeArea
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private func calculateDuctMetrics(for airflow: Double) -> (diameter: Int, velocity: Double) {
|
||||
let ductArea = airflow / Self.targetVelocity
|
||||
let ductDiameter = sqrt((4 * ductArea) / Double.pi) * 12
|
||||
let standardDuctSize = getStandardDuctSize(for: Int(ductDiameter))
|
||||
let actualVelocity = airflow / (Double.pi * pow(Double(standardDuctSize) / 24, 2))
|
||||
|
||||
return (standardDuctSize, actualVelocity)
|
||||
}
|
||||
|
||||
private func generateWarnings(
|
||||
roomPressure: Double,
|
||||
actualVelocity: Double,
|
||||
totalLeakageArea: Double,
|
||||
netFreeArea: Double
|
||||
) -> [String] {
|
||||
var warnings = [String]()
|
||||
|
||||
if roomPressure > 3 {
|
||||
warnings.append(
|
||||
"Room pressure exceeds 3 Pascals - consider reducing pressure differntial."
|
||||
)
|
||||
}
|
||||
|
||||
if totalLeakageArea > netFreeArea / 144 * 0.5 {
|
||||
warnings.append(
|
||||
"Door leakage area is significant - consider reducing gaps or increasing grille size."
|
||||
)
|
||||
}
|
||||
|
||||
if actualVelocity > Self.maxVelocity {
|
||||
warnings.append(
|
||||
"Return air velocity exceeds maximum recommended velocity - consider next size up duct."
|
||||
)
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
private func getStandardDuctSize(for calculatedSize: Int) -> Int {
|
||||
@@ -74,3 +183,40 @@ public extension RoomPressure.Request {
|
||||
case invalidPreferredHeight
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomPressure.Request.KnownAirflow {
|
||||
func validate() throws {
|
||||
guard targetRoomPressure > 0 else {
|
||||
throw ValidationError(message: "Target room pressure should be greater than 0.")
|
||||
}
|
||||
guard doorWidth > 0 else {
|
||||
throw ValidationError(message: "Door width should be greater than 0.")
|
||||
}
|
||||
guard doorHeight > 0 else {
|
||||
throw ValidationError(message: "Door height should be greater than 0.")
|
||||
}
|
||||
guard doorUndercut > 0 else {
|
||||
throw ValidationError(message: "Door undercut should be greater than 0.")
|
||||
}
|
||||
guard supplyAirflow > 0 else {
|
||||
throw ValidationError(message: "Supply airflow should be greater than 0.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomPressure.Request.MeasuredPressure {
|
||||
func validate() throws {
|
||||
guard measuredRoomPressure > 0 else {
|
||||
throw ValidationError(message: "MeasuredPressure room pressure should be greater than 0.")
|
||||
}
|
||||
guard doorWidth > 0 else {
|
||||
throw ValidationError(message: "Door width should be greater than 0.")
|
||||
}
|
||||
guard doorHeight > 0 else {
|
||||
throw ValidationError(message: "Door height should be greater than 0.")
|
||||
}
|
||||
guard doorUndercut > 0 else {
|
||||
throw ValidationError(message: "Door undercut should be greater than 0.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Sources/ApiController/ValidationError.swift
Normal file
3
Sources/ApiController/ValidationError.swift
Normal file
@@ -0,0 +1,3 @@
|
||||
struct ValidationError: Error {
|
||||
let message: String
|
||||
}
|
||||
@@ -87,10 +87,10 @@ public enum RoomPressure {
|
||||
|
||||
public struct DuctSize: Codable, Equatable, Sendable {
|
||||
|
||||
public let diameter: Double
|
||||
public let diameter: Int
|
||||
public let velocity: Double
|
||||
|
||||
public init(diameter: Double, velocity: Double) {
|
||||
public init(diameter: Int, velocity: Double) {
|
||||
self.diameter = diameter
|
||||
self.velocity = velocity
|
||||
}
|
||||
@@ -99,11 +99,11 @@ public enum RoomPressure {
|
||||
|
||||
public struct GrilleSize: Codable, Equatable, Sendable {
|
||||
|
||||
public let width: Double
|
||||
public let height: Double
|
||||
public let width: Int
|
||||
public let height: Int
|
||||
public let area: Double
|
||||
|
||||
public init(width: Double, height: Double, area: Double) {
|
||||
public init(width: Int, height: Int, area: Double) {
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.area = area
|
||||
|
||||
Reference in New Issue
Block a user