Files
swift-estimated-pressures-core/Sources/SharedModels/EquipmentMeasurement.swift

390 lines
12 KiB
Swift

import Foundation
public enum EquipmentMeasurement: Equatable {
case airHandler(AirHandler)
case furnaceAndCoil(FurnaceAndCoil)
public var equipmentType: EquipmentType {
switch self {
case .airHandler:
return .airHandler
case .furnaceAndCoil:
return .furnaceAndCoil
}
}
public var externalStaticPressure: Positive<Double> {
switch self {
case let .airHandler(airHandler):
return airHandler.externalStaticPressure
case let .furnaceAndCoil(furnaceAndCoil):
return furnaceAndCoil.externalStaticPressure
}
}
public var manufacturersIncludedFilterPressureDrop: Positive<Double> {
switch self {
case let .airHandler(airHandler):
return airHandler.$manufacturersIncludedFilterPressureDrop
case let .furnaceAndCoil(furnaceAndCoil):
return furnaceAndCoil.$manufacturersIncludedFilterPressureDrop
}
}
public struct AirHandler: Equatable {
@Positive
public var airflow: Double
@Positive
public var manufacturersIncludedFilterPressureDrop: Double
@Positive
public var returnPlenumPressure: Double
@Positive
public var postFilterPressure: Double
@Positive
public var postCoilPressure: Double
@Positive
public var supplyPlenumPressure: Double
public init(
airflow: Double,
manufacturersIncludedFilterPressureDrop: Double,
returnPlenumPressure: Double,
postFilterPressure: Double,
postCoilPressure: Double,
supplyPlenumPressure: Double
) {
self.airflow = airflow
self.manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
self.returnPlenumPressure = returnPlenumPressure
self.postFilterPressure = postFilterPressure
self.postCoilPressure = postCoilPressure
self.supplyPlenumPressure = supplyPlenumPressure
}
public init(
airflow: Positive<Double>,
manufacturersIncludedFilterPressureDrop: Positive<Double>,
returnPlenumPressure: Positive<Double>,
postFilterPressure: Positive<Double>,
postCoilPressure: Positive<Double>,
supplyPlenumPressure: Positive<Double>
) {
self._airflow = airflow
self._manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
self._returnPlenumPressure = returnPlenumPressure
self._postFilterPressure = postFilterPressure
self._postCoilPressure = postCoilPressure
self._supplyPlenumPressure = supplyPlenumPressure
}
public var externalStaticPressure: Positive<Double> {
var postFilterAdder = Positive<Double>.zero
if $postFilterPressure > $manufacturersIncludedFilterPressureDrop {
postFilterAdder = $postFilterPressure - $manufacturersIncludedFilterPressureDrop
}
return $returnPlenumPressure
+ postFilterAdder
+ $supplyPlenumPressure
}
}
public enum EquipmentType: Equatable, CaseIterable, CustomStringConvertible, Identifiable {
case airHandler
case furnaceAndCoil
public var id: Self { self }
public var description: String {
switch self {
case .airHandler:
return "Air Handler"
case .furnaceAndCoil:
return "Furnace & Coil"
}
}
}
// TODO: Needs updated for when forms are using `minmal` values.
public struct FlaggedMeasurement: Equatable {
public var airflow: Flagged
public var coilPressureDrop: Flagged
public var externalStaticPressure: Flagged
public var filterPressureDrop: Flagged
public var returnPlenumPressure: Flagged
public var supplyPlenumPressure: Flagged
public init(
airflow: Flagged,
coilPressureDrop: Flagged,
externalStaticPressure: Flagged,
filterPressureDrop: Flagged,
returnPlenumPressure: Flagged,
supplyPlenumPressure: Flagged
) {
self.airflow = airflow
self.coilPressureDrop = coilPressureDrop
self.externalStaticPressure = externalStaticPressure
self.filterPressureDrop = filterPressureDrop
self.returnPlenumPressure = returnPlenumPressure
self.supplyPlenumPressure = supplyPlenumPressure
}
public init(
budgets: BudgetedPercentEnvelope,
measurement: EquipmentMeasurement,
ratedPressures: RatedStaticPressures,
tons: EquipmentMetadata.CoolingCapacity?
) {
switch measurement {
case let .airHandler(airHandler):
self = .airHandler(
budgets: budgets,
measurement: airHandler,
ratedPressures: ratedPressures,
tons: tons
)
case let .furnaceAndCoil(furnaceAndCoil):
self = .furnaceAndCoil(
budgets: budgets,
measurement: furnaceAndCoil,
ratedPressures: ratedPressures,
tons: tons
)
}
}
public static func airHandler(
budgets: BudgetedPercentEnvelope,
measurement: EquipmentMeasurement.AirHandler,
ratedPressures: RatedStaticPressures,
tons: EquipmentMetadata.CoolingCapacity?
) -> Self {
.init(
airflow: checkAirflow(value: measurement.airflow, tons: tons),
coilPressureDrop: .init(
wrappedValue: (measurement.$postCoilPressure.positiveValue - measurement.$postFilterPressure.positiveValue),
.result(.good())
),
externalStaticPressure: checkExternalStaticPressure(
value: measurement.externalStaticPressure,
ratedPressures: ratedPressures
),
filterPressureDrop: .init(
value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue,
budget: budgets.filterBudget,
ratedPressures: ratedPressures,
ignoreMinimum: true
),
returnPlenumPressure: .init(
value: measurement.$returnPlenumPressure.positiveValue,
budget: budgets.returnPlenumBudget,
ratedPressures: ratedPressures
),
supplyPlenumPressure: .init(
value: measurement.$supplyPlenumPressure.positiveValue,
budget: budgets.supplyPlenumBudget,
ratedPressures: ratedPressures
)
)
}
public static func furnaceAndCoil(
budgets: BudgetedPercentEnvelope,
measurement: EquipmentMeasurement.FurnaceAndCoil,
ratedPressures: RatedStaticPressures,
tons: EquipmentMetadata.CoolingCapacity?
) -> Self {
.init(
airflow: checkAirflow(value: measurement.airflow, tons: tons),
coilPressureDrop: .init(
value: measurement.$preCoilPressure.positiveValue - measurement.$supplyPlenumPressure.positiveValue,
budget: budgets.coilBudget,
ratedPressures: ratedPressures
),
externalStaticPressure: checkExternalStaticPressure(
value: measurement.externalStaticPressure,
ratedPressures: ratedPressures
),
filterPressureDrop: .init(
value: measurement.$postFilterPressure.positiveValue - measurement.$returnPlenumPressure.positiveValue,
budget: budgets.filterBudget,
ratedPressures: ratedPressures,
ignoreMinimum: true
),
returnPlenumPressure: .init(
value: measurement.$returnPlenumPressure.positiveValue,
budget: budgets.returnPlenumBudget,
ratedPressures: ratedPressures
),
supplyPlenumPressure: .init(
value: measurement.$supplyPlenumPressure.positiveValue,
budget: budgets.supplyPlenumBudget,
ratedPressures: ratedPressures
)
)
}
}
public struct FurnaceAndCoil: Equatable {
@Positive
public var airflow: Double
@Positive
public var manufacturersIncludedFilterPressureDrop: Double
@Positive
public var returnPlenumPressure: Double
@Positive
public var postFilterPressure: Double
@Positive
public var preCoilPressure: Double
@Positive
public var supplyPlenumPressure: Double
public init(
airflow: Double,
manufacturersIncludedFilterPressureDrop: Double,
returnPlenumPressure: Double,
postFilterPressure: Double,
preCoilPressure: Double,
supplyPlenumPressure: Double
) {
self.airflow = airflow
self.manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
self.returnPlenumPressure = returnPlenumPressure
self.postFilterPressure = postFilterPressure
self.preCoilPressure = preCoilPressure
self.supplyPlenumPressure = supplyPlenumPressure
}
public init(
airflow: Positive<Double>,
manufacturersIncludedFilterPressureDrop: Positive<Double>,
returnPlenumPressure: Positive<Double>,
postFilterPressure: Positive<Double>,
preCoilPressure: Positive<Double>,
supplyPlenumPressure: Positive<Double>
) {
self._airflow = airflow
self._manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop
self._returnPlenumPressure = returnPlenumPressure
self._postFilterPressure = postFilterPressure
self._preCoilPressure = preCoilPressure
self._supplyPlenumPressure = supplyPlenumPressure
}
public var externalStaticPressure: Positive<Double> {
($postFilterPressure - $manufacturersIncludedFilterPressureDrop) + $preCoilPressure
}
}
}
fileprivate extension Flagged {
init(
value: Double,
budget: Percentage,
ratedPressures: RatedStaticPressures,
ignoreMinimum: Bool = false
) {
let minimum = ignoreMinimum ? 0 : ratedPressures.minimum * budget.fraction
let maximum = ratedPressures.maximum * budget.fraction
let rated = ratedPressures.rated * budget.fraction
self.init(
wrappedValue: value,
.using(maximum: maximum, minimum: minimum, rated: rated)
)
}
init(
value: Double?,
budget: Percentage,
ratedPressures: RatedStaticPressures,
ignoreMinimum: Bool = false
) {
guard let value else {
self = .error(message: "Value is not set.")
return
}
self.init(
value: value,
budget: budget,
ratedPressures: ratedPressures,
ignoreMinimum: ignoreMinimum
)
}
}
fileprivate func checkExternalStaticPressure(
value: Positive<Double>,
ratedPressures: RatedStaticPressures
) -> Flagged {
.init(
wrappedValue: value.positiveValue,
.rated(ratedPressures)
)
}
fileprivate func checkAirflow(
value: Double?,
tons: EquipmentMetadata.CoolingCapacity?
) -> Flagged {
guard let value, let tons else {
return .init(wrappedValue: value ?? 0, .result(.good()))
}
return .init(wrappedValue: value, .airflow(tons: tons))
}
#if DEBUG
extension EquipmentMeasurement {
public static func mock(type equipmentType: EquipmentType) -> Self {
switch equipmentType {
case .airHandler:
return .airHandler(.init(
airflow: 1200,
manufacturersIncludedFilterPressureDrop: 0.1,
returnPlenumPressure: 0.3,
postFilterPressure: 0.6,
postCoilPressure: 0.9,
supplyPlenumPressure: 0.2
))
case .furnaceAndCoil:
return .furnaceAndCoil(.init(
airflow: 1200,
manufacturersIncludedFilterPressureDrop: 0.0,
returnPlenumPressure: 0.3,
postFilterPressure: 0.6,
preCoilPressure: 0.4,
supplyPlenumPressure: 0.1
))
}
}
}
extension EquipmentMeasurement.FlaggedMeasurement {
public static func mock(type equipmentType: EquipmentMeasurement.EquipmentType) -> Self {
.init(
budgets: .init(equipmentType: equipmentType, fanType: .variableSpeed),
measurement: .mock(type: equipmentType),
ratedPressures: .init(),
tons: .default
)
}
}
#endif