Files

166 lines
5.6 KiB
Swift

import Foundation
import Logging
import OrderedCollections
import Routes
public extension AtticVentilation.Request {
private static let stackEffectCoefficient = 0.0188
private static let windEffectCoefficient = 0.0299
private static let minimumVentRatio = 1.0 / 300.0
private static let recommendedVentRatio = 1.0 / 150
private static let intakeToExhaustRatio = 1.5
// swiftlint:disable function_body_length
func respond(logger: Logger) async throws -> AtticVentilation.Response {
try validate()
var recommendations = [String]()
var warnings = [String]()
var ventilationStatus: AtticVentilation.VentilationStatus = .adequate
let temperatureDifference = checkTemperatureDifferential(
ventilationStatus: &ventilationStatus,
warnings: &warnings
)
let dewpointDifference = atticDewpoint - outdoorDewpoint
let stackEffect = Self.stackEffectCoefficient * atticFloorArea * sqrt(abs(temperatureDifference))
* (temperatureDifference < 0 ? -1 : 1)
let minimumVentArea = atticFloorArea * Self.minimumVentRatio
let recommendVentArea = atticFloorArea * Self.recommendedVentRatio
let requiredIntake = (recommendVentArea * Self.intakeToExhaustRatio) / (1 + Self.intakeToExhaustRatio)
let requiredExhaust = recommendVentArea - requiredIntake
if abs(pressureDifferential) > 2 {
warnings.append(
"High pressure differential - may indicate blocked or inadequate ventilation"
)
if ventilationStatus != .critical {
ventilationStatus = .inadequate
}
}
if abs(dewpointDifference) > 5 {
warnings.append(
"High moisture levels in the attic - increased ventilation is recommended"
)
if ventilationStatus != .critical {
ventilationStatus = .inadequate
}
}
// compare existing ventilation to requirements.
checkExistingAreas(
minimumVentArea: minimumVentArea,
recommendVentArea: recommendVentArea,
requiredIntake: requiredIntake,
requiredExhaust: requiredExhaust,
recommendations: &recommendations,
ventilationStatus: &ventilationStatus,
warnings: &warnings
)
// Pressure recommendations
addPressureRecommendations(&recommendations)
return .init(
pressureDifferential: pressureDifferential,
temperatureDifferential: temperatureDifference,
dewpointDifferential: dewpointDifference,
stackEffect: stackEffect,
requiredVentilationArea: .init(intake: requiredIntake, exhaust: requiredExhaust),
recommendations: recommendations,
warnings: warnings,
ventilationStatus: ventilationStatus
)
}
// swiftlint:enable function_body_length
private func checkTemperatureDifferential(
ventilationStatus: inout AtticVentilation.VentilationStatus,
warnings: inout [String]
) -> Double {
let temperatureDifference = atticTemperature - outdoorTemperature
let absTempDiff = abs(temperatureDifference)
if absTempDiff > 20 {
warnings.append(
"High temperature differential - indicates insufficient ventilation."
)
ventilationStatus = .inadequate
}
if absTempDiff > 30 {
warnings.append(
"Critical temperature differential - immediate action recommended"
)
ventilationStatus = .critical
}
return temperatureDifference
}
// swiftlint:disable function_parameter_count
private func checkExistingAreas(
minimumVentArea: Double,
recommendVentArea: Double,
requiredIntake: Double,
requiredExhaust: Double,
recommendations: inout [String],
ventilationStatus: inout AtticVentilation.VentilationStatus,
warnings: inout [String]
) {
if let existingIntakeArea, let existingExhaustArea {
let totalExisting = existingIntakeArea + existingExhaustArea
if totalExisting < minimumVentArea {
warnings.append("Existing ventilation area below minimum requirement.")
if ventilationStatus != .critical {
ventilationStatus = .inadequate
}
} else if totalExisting < recommendVentArea {
recommendations.append("Consider increasing ventilation area to meet recommended guidelines.")
}
if existingIntakeArea < requiredIntake {
recommendations.append(
"Add \(double: requiredIntake - existingIntakeArea, fractionDigits: 1) ft² of intake ventilation."
)
}
if existingExhaustArea < requiredExhaust {
recommendations.append(
"Add \(double: requiredExhaust - existingExhaustArea, fractionDigits: 1) ft² of exhaust ventilation."
)
}
} else {
recommendations.append(
"Add \(double: requiredIntake, fractionDigits: 1) ft² of intake ventilation."
)
recommendations.append(
"Add \(double: requiredExhaust, fractionDigits: 1) ft² of exhaust ventilation."
)
}
}
// swiftlint:enable function_parameter_count
private func addPressureRecommendations(_ recommendations: inout [String]) {
if pressureDifferential > 0 {
recommendations.append("Consider adding more exhaust ventilation to balance pressure.")
} else if pressureDifferential < 0 {
recommendations.append("Consider adding more intake ventilation to balance pressure.")
}
}
private func validate() throws {
guard atticFloorArea > 0 else {
throw ValidationError(message: "Attic floor area should be greater than 0.")
}
if let existingIntakeArea, existingIntakeArea < 0 {
throw ValidationError(message: "Existing intake area should be greater than 0.")
}
if let existingExhaustArea, existingExhaustArea < 0 {
throw ValidationError(message: "Existing exhaust area should be greater than 0.")
}
}
}