feat: Adds initial room pressure calculations.

This commit is contained in:
2025-03-01 01:02:03 -05:00
parent af24ef3971
commit 5630820575
3 changed files with 155 additions and 6 deletions

View File

@@ -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.")
}
}
}

View File

@@ -0,0 +1,3 @@
struct ValidationError: Error {
let message: String
}