166 lines
5.6 KiB
Swift
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.")
|
|
}
|
|
}
|
|
}
|