From be32b915dcd723786bedd678c363dc4e0ea94d52 Mon Sep 17 00:00:00 2001 From: kevin-moos Date: Sun, 24 May 2026 19:13:20 -0500 Subject: [PATCH 1/4] Check for a ventilation setting when no loads are met --- src/EnergyPlus/HybridEvapCoolingModel.cc | 248 +++++++++++++++-------- src/EnergyPlus/HybridEvapCoolingModel.hh | 2 + 2 files changed, 162 insertions(+), 88 deletions(-) diff --git a/src/EnergyPlus/HybridEvapCoolingModel.cc b/src/EnergyPlus/HybridEvapCoolingModel.cc index cb3c8f72ca3..7558ceb1b95 100644 --- a/src/EnergyPlus/HybridEvapCoolingModel.cc +++ b/src/EnergyPlus/HybridEvapCoolingModel.cc @@ -373,19 +373,31 @@ namespace HybridEvapCoolingModel { if (Min_Msa == Max_Msa) { sol.MassFlowRatio.push_back(Max_Msa); } else { - Real64 ResolutionMsa = (Max_Msa - Min_Msa) * 0.2; - for (Real64 Msa_val = Max_Msa; Msa_val >= Min_Msa; Msa_val -= ResolutionMsa) { + int Resolution = 5; + Real64 ResolutionMsa = (Max_Msa - Min_Msa) / Resolution; + + // ensure that the min/max values are included in the solution space + sol.MassFlowRatio.push_back(Max_Msa); + for (int i = Resolution - 1; i > 0; i--) { + Real64 Msa_val = Min_Msa + ResolutionMsa * i; sol.MassFlowRatio.push_back(Msa_val); } + sol.MassFlowRatio.push_back(Min_Msa); } if (Min_OAF == Max_OAF) { sol.OutdoorAirFraction.push_back(Max_OAF); } else { - Real64 ResolutionOSA = (Max_OAF - Min_OAF) * 0.2; - for (Real64 OAF_val = Max_OAF; OAF_val >= Min_OAF; OAF_val -= ResolutionOSA) { + int Resolution = 5; + Real64 ResolutionOAF = (Max_OAF - Min_OAF) / Resolution; + + // ensure that the min/max values are included in the solution space + sol.OutdoorAirFraction.push_back(Max_OAF); + for (int i = Resolution - 1; i > 0; i--) { + Real64 OAF_val = Min_OAF + ResolutionOAF * i; sol.OutdoorAirFraction.push_back(OAF_val); } + sol.OutdoorAirFraction.push_back(Min_OAF); } } @@ -828,6 +840,7 @@ namespace HybridEvapCoolingModel { RunningPeakCapacity_EnvCondMet = false; Settings.clear(); + VentilationSettings.clear(); } void Model::Initialize(int ZoneNumber) @@ -1045,6 +1058,85 @@ namespace HybridEvapCoolingModel { return averagedVal; } + void Model::CalculateSettingOutputs(EnergyPlusData &state, CSetting &Setting, CStepInputs StepIns, Real64 Wosa, Real64 Wra, Real64 MinOA_Msa) + { + // Calculate the delta H + Real64 OSAF = Setting.Outdoor_Air_Fraction; + Real64 UnscaledMsa = Setting.Unscaled_Supply_Air_Mass_Flow_Rate; + Real64 ScaledMsa = Setting.ScaledSupply_Air_Mass_Flow_Rate; + + // send the scaled Msa to calculate energy and the unscaled for sending to curves. + Real64 Tsa = Setting.SupplyAirTemperature; + Real64 Wsa = Setting.SupplyAirW; + Real64 Tma = StepIns.Tra + OSAF * (StepIns.Tosa - StepIns.Tra); + Real64 Wma = Wra + OSAF * (Wosa - Wra); + Setting.Mixed_Air_Temperature = Tma; + Setting.Mixed_Air_W = Wma; + + Real64 Hma = PsyHFnTdbW(Tma, Wma); + // Calculate Enthalpy of return air + Real64 Hra = PsyHFnTdbW(StepIns.Tra, Wra); + Real64 Hsa = PsyHFnTdbW(Tsa, Wsa); + + Real64 SupplyAirCp = PsyCpAirFnW(Wsa); // J/degreesK.kg + Real64 ReturnAirCP = PsyCpAirFnW(Wra); // J/degreesK.kg + Real64 OutdoorAirCP = PsyCpAirFnW(Wosa); // J/degreesK.kg + + // Calculations below of system cooling and heating capacity are ultimately reassessed when the resultant part runtime fraction is + // assessed. However its valuable that they are calculated here to at least provide a check. + + // System Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA + OSAF*(cpOSA-cpRA) + cpSA) {kJ/kg-C} * (T_RA + OSAF*(T_OSA - T_RA) - T_SA) + // System Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA + OSAF *(HR_OSA - HR_RA) - HR_SA) {kgWater/kgDryAir} + // System Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA + OSAF*(h_OSA - h_RA) - h_SA) {kJ/kgDryAir} + Real64 SystemCp = ReturnAirCP + OSAF * (OutdoorAirCP - ReturnAirCP) + SupplyAirCp; // J/degreesK.kg + Real64 SensibleSystem = ScaledMsa * 0.5 * SystemCp * (Tma - Tsa); // W dynamic cp + Real64 MsaDry = ScaledMsa * (1 - Wsa); + Real64 LambdaSa = Psychrometrics::PsyHfgAirFnWTdb(0, Tsa); + Real64 LatentSystem = LambdaSa * MsaDry * (Wma - Wsa); // W + // Total system cooling + Setting.TotalSystem = (Hma - Hsa) * ScaledMsa; + // Perform latent check + // Real64 latentCheck = TotalSystem - SensibleSystem; + + // Zone Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA+cpSA) {kJ/kg-C} * (T_RA - T_SA) {C} + // Zone Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA - HR_SA) {kgWater/kgDryAir} + // Zone Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA - h_SA) {kJ/kgDryAir} + Real64 SensibleRoomORZone = ScaledMsa * 0.5 * (SupplyAirCp + ReturnAirCP) * (StepIns.Tra - Tsa); // W dynamic cp + Real64 latentRoomORZone = LambdaSa * MsaDry * (Wra - Wsa); // W + // Total room cooling + Real64 TotalRoomORZone = (Hra - Hsa) * ScaledMsa; // W + // Perform latent check + // Real64 latentRoomORZoneCheck = TotalRoomORZone - SensibleRoomORZone; + + Setting.SensibleSystem = SensibleSystem; + Setting.LatentSystem = LatentSystem; + Setting.TotalZone = TotalRoomORZone; + Setting.SensibleZone = SensibleRoomORZone; + Setting.LatentZone = latentRoomORZone; + + Setting.ElectricalPower = Setting.oMode.CalculateCurveVal( + state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, POWER_CURVE); // [Kw] calculations for fuel in Kw + Setting.SupplyFanElectricPower = + Setting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, SUPPLY_FAN_POWER); + Setting.ExternalStaticPressure = + Setting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, EXTERNAL_STATIC_PRESSURE); + Setting.SecondaryFuelConsumptionRate = + Setting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, SECOND_FUEL_USE); + Setting.ThirdFuelConsumptionRate = + Setting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, THIRD_FUEL_USE); + Setting.WaterConsumptionRate = Setting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, WATER_USE); + + // Calculate partload fraction required to meet all requirements + Setting.Runtime_Fraction = Model::CalculatePartRuntimeFraction(MinOA_Msa, + Setting.Supply_Air_Ventilation_Volume * state.dataEnvrn->StdRhoAir, + StepIns.RequestedCoolingLoad, + StepIns.RequestedHeatingLoad, + SensibleRoomORZone, + StepIns.ZoneDehumidificationLoad, + StepIns.ZoneMoistureLoad, + latentRoomORZone); + } + Real64 Model::CalculatePartRuntimeFraction(Real64 MinOA_Msa, Real64 Mvent, Real64 RequestedCoolingLoad, @@ -1192,12 +1284,9 @@ namespace HybridEvapCoolingModel { // SUBROUTINE LOCAL VARIABLE DECLARATIONS: bool DidWeMeetLoad = false; bool DidWeMeetHumidification = false; + bool DidWeMeetVentilation = false; bool DidWePartlyMeetLoad = false; Real64 OptimalSetting_RunFractionTotalFuel = IMPLAUSIBLE_POWER; - Real64 Tma; - Real64 Wma; - Real64 Hsa; - Real64 Hma; Real64 PreviousMaxiumConditioningOutput = 0; Real64 PreviousMaxiumHumidOrDehumidOutput = 0; std::string ObjectID = Name.c_str(); @@ -1342,8 +1431,21 @@ namespace HybridEvapCoolingModel { CandidateSetting.oMode = Mode; CandidateSetting.SupplyAirTemperature = Tsa; CandidateSetting.SupplyAirW = CheckVal_W(state, Wsa, Tsa, OutletPressure); - CandidateSetting.Mode = Mode.ModeID; Settings.push_back(CandidateSetting); + + CSetting VentilationSetting; + VentilationSetting.Supply_Air_Ventilation_Volume = Supply_Air_Ventilation_Volume; + VentilationSetting.Mode = Mode.ModeID; + VentilationSetting.Outdoor_Air_Fraction = OSAF; + VentilationSetting.Supply_Air_Mass_Flow_Rate_Ratio = MsaRatio; + VentilationSetting.Unscaled_Supply_Air_Mass_Flow_Rate = min(MinOA_Msa, UnscaledMsa); + VentilationSetting.ScaledSupply_Air_Mass_Flow_Rate = min(MinOA_Msa, ScaledMsa); + VentilationSetting.ScaledSupply_Air_Ventilation_Volume = + VentilationSetting.ScaledSupply_Air_Mass_Flow_Rate / state.dataEnvrn->StdRhoAir; + VentilationSetting.oMode = Mode; + VentilationSetting.SupplyAirTemperature = StepIns.Tosa + FanHeatTemp; + VentilationSetting.SupplyAirW = CheckVal_W(state, Wsa, VentilationSetting.SupplyAirTemperature, OutletPressure); + VentilationSettings.push_back(VentilationSetting); } } } @@ -1362,66 +1464,13 @@ namespace HybridEvapCoolingModel { } for (auto &thisSetting : Settings) { - // Calculate the delta H - Real64 OSAF = thisSetting.Outdoor_Air_Fraction; - Real64 UnscaledMsa = thisSetting.Unscaled_Supply_Air_Mass_Flow_Rate; - Real64 ScaledMsa = thisSetting.ScaledSupply_Air_Mass_Flow_Rate; - - // send the scaled Msa to calculate energy and the unscaled for sending to curves. - Tsa = thisSetting.SupplyAirTemperature; - Wsa = thisSetting.SupplyAirW; - Tma = StepIns.Tra + OSAF * (StepIns.Tosa - StepIns.Tra); - Wma = Wra + OSAF * (Wosa - Wra); - thisSetting.Mixed_Air_Temperature = Tma; - thisSetting.Mixed_Air_W = Wma; - - Hma = PsyHFnTdbW(Tma, Wma); - // Calculate Enthalpy of return air - Real64 Hra = PsyHFnTdbW(StepIns.Tra, Wra); - - Hsa = PsyHFnTdbW(Tsa, Wsa); - - Real64 SupplyAirCp = PsyCpAirFnW(Wsa); // J/degreesK.kg - Real64 ReturnAirCP = PsyCpAirFnW(Wra); // J/degreesK.kg - Real64 OutdoorAirCP = PsyCpAirFnW(Wosa); // J/degreesK.kg - - // Calculations below of system cooling and heating capacity are ultimately reassessed when the resultant part runtime fraction is - // assessed. However its valuable that they are calculated here to at least provide a check. - - // System Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA + OSAF*(cpOSA-cpRA) + cpSA) {kJ/kg-C} * (T_RA + OSAF*(T_OSA - T_RA) - T_SA) - // System Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA + OSAF *(HR_OSA - HR_RA) - HR_SA) {kgWater/kgDryAir} - // System Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA + OSAF*(h_OSA - h_RA) - h_SA) {kJ/kgDryAir} - Real64 SystemCp = ReturnAirCP + OSAF * (OutdoorAirCP - ReturnAirCP) + SupplyAirCp; // J/degreesK.kg - Real64 SensibleSystem = ScaledMsa * 0.5 * SystemCp * (Tma - Tsa); // W dynamic cp - Real64 MsaDry = ScaledMsa * (1 - Wsa); - Real64 LambdaSa = Psychrometrics::PsyHfgAirFnWTdb(0, Tsa); - Real64 LatentSystem = LambdaSa * MsaDry * (Wma - Wsa); // W - // Total system cooling - thisSetting.TotalSystem = (Hma - Hsa) * ScaledMsa; - // Perform latent check - // Real64 latentCheck = TotalSystem - SensibleSystem; - - // Zone Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA+cpSA) {kJ/kg-C} * (T_RA - T_SA) {C} - // Zone Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA - HR_SA) {kgWater/kgDryAir} - // Zone Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA - h_SA) {kJ/kgDryAir} - Real64 SensibleRoomORZone = ScaledMsa * 0.5 * (SupplyAirCp + ReturnAirCP) * (StepIns.Tra - Tsa); // W dynamic cp - Real64 latentRoomORZone = LambdaSa * MsaDry * (Wra - Wsa); // W - // Total room cooling - Real64 TotalRoomORZone = (Hra - Hsa) * ScaledMsa; // W - // Perform latent check - // Real64 latentRoomORZoneCheck = TotalRoomORZone - SensibleRoomORZone; - - thisSetting.SensibleSystem = SensibleSystem; - thisSetting.LatentSystem = LatentSystem; - thisSetting.TotalZone = TotalRoomORZone; - thisSetting.SensibleZone = SensibleRoomORZone; - thisSetting.LatentZone = latentRoomORZone; + CalculateSettingOutputs(state, thisSetting, StepIns, Wosa, Wra, MinOA_Msa); bool Conditioning_load_met = false; - if (CoolingRequested && (SensibleRoomORZone > StepIns.RequestedCoolingLoad)) { + if (CoolingRequested && (thisSetting.SensibleZone > StepIns.RequestedCoolingLoad)) { Conditioning_load_met = true; } - if (HeatingRequested && (SensibleRoomORZone < StepIns.RequestedHeatingLoad)) { + if (HeatingRequested && (thisSetting.SensibleZone < StepIns.RequestedHeatingLoad)) { Conditioning_load_met = true; } if (!(HeatingRequested || CoolingRequested)) { @@ -1429,39 +1478,26 @@ namespace HybridEvapCoolingModel { } bool Humidification_load_met = false; - if (DehumidificationRequested && latentRoomORZone > StepIns.ZoneDehumidificationLoad) { + if (DehumidificationRequested && thisSetting.LatentZone > StepIns.ZoneDehumidificationLoad) { Humidification_load_met = true; } - if (HumidificationRequested && latentRoomORZone < StepIns.ZoneMoistureLoad) { + if (HumidificationRequested && thisSetting.LatentZone < StepIns.ZoneMoistureLoad) { Humidification_load_met = true; } if (!(HumidificationRequested || DehumidificationRequested)) { Humidification_load_met = true; } - thisSetting.ElectricalPower = thisSetting.oMode.CalculateCurveVal( - state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, POWER_CURVE); // [Kw] calculations for fuel in Kw - thisSetting.SupplyFanElectricPower = - thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, SUPPLY_FAN_POWER); - thisSetting.ExternalStaticPressure = - thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, EXTERNAL_STATIC_PRESSURE); - thisSetting.SecondaryFuelConsumptionRate = - thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, SECOND_FUEL_USE); - thisSetting.ThirdFuelConsumptionRate = - thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, THIRD_FUEL_USE); - thisSetting.WaterConsumptionRate = - thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, WATER_USE); - // Calculate partload fraction required to meet all requirements // Fraction can be above 1 meaning its not able to do it completely in a time step Real64 PartRuntimeFraction = CalculatePartRuntimeFraction(MinOA_Msa, thisSetting.Supply_Air_Ventilation_Volume * state.dataEnvrn->StdRhoAir, StepIns.RequestedCoolingLoad, StepIns.RequestedHeatingLoad, - SensibleRoomORZone, + thisSetting.SensibleZone, StepIns.ZoneDehumidificationLoad, StepIns.ZoneMoistureLoad, - latentRoomORZone); + thisSetting.LatentZone); Real64 RunFractionTotalFuel; switch (ObjectiveFunction) { @@ -1495,24 +1531,28 @@ namespace HybridEvapCoolingModel { if (Conditioning_load_met) { DidWeMeetLoad = true; - if (HumidificationRequested && (latentRoomORZone < PreviousMaxiumHumidOrDehumidOutput)) { + if (HumidificationRequested && (thisSetting.LatentZone < PreviousMaxiumHumidOrDehumidOutput)) { store_best_attempt = true; } - if (DehumidificationRequested && (latentRoomORZone > PreviousMaxiumHumidOrDehumidOutput)) { + if (DehumidificationRequested && (thisSetting.LatentZone > PreviousMaxiumHumidOrDehumidOutput)) { store_best_attempt = true; } if (store_best_attempt) { - PreviousMaxiumHumidOrDehumidOutput = latentRoomORZone; + PreviousMaxiumHumidOrDehumidOutput = thisSetting.LatentZone; } } else { - if (CoolingRequested && (SensibleRoomORZone > PreviousMaxiumConditioningOutput)) { + if (CoolingRequested && (thisSetting.SensibleZone > PreviousMaxiumConditioningOutput)) { store_best_attempt = true; } - if (HeatingRequested && (SensibleRoomORZone < PreviousMaxiumConditioningOutput)) { + if (HeatingRequested && (thisSetting.SensibleZone < PreviousMaxiumConditioningOutput)) { store_best_attempt = true; } if (store_best_attempt) { - PreviousMaxiumConditioningOutput = SensibleRoomORZone; + PreviousMaxiumConditioningOutput = thisSetting.SensibleZone; + } else if (VentilationRequested) { + // Ventilation requirements have already been met or we wouldn't be here (see MinVRMet above) + // Set this for later, in case no loads are met (even partially) + DidWeMeetVentilation = true; } } if (store_best_attempt) { @@ -1551,6 +1591,38 @@ namespace HybridEvapCoolingModel { } else { // if we partly met the load then do the best we can and run full out in that optimal setting. if (DidWePartlyMeetLoad) { + ErrorCode = 0; + count_DidWeNotMeetLoad++; + if (OptimalSetting.ElectricalPower == IMPLAUSIBLE_POWER) { + ShowWarningError(state, "Model was not able to provide cooling for a time step, called in HybridEvapCooling:dostep"); + OptimalSetting.ElectricalPower = 0; + } + OptimalSetting.Runtime_Fraction = 1; + CurrentOperatingSettings[0] = OptimalSetting; + PrimaryMode = OptimalSetting.Mode; + PrimaryModeRuntimeFraction = 1; + } // if we didn't meet any loads but still met ventilation, calculate the ventilation settings and select an appropriate candidate + else if (DidWeMeetVentilation) { + for (auto &thisSetting : VentilationSettings) { + CalculateSettingOutputs(state, thisSetting, StepIns, Wosa, Wra, MinOA_Msa); + } + std::sort(VentilationSettings.begin(), VentilationSettings.end(), [](const CSetting &a, const CSetting &b) { + if (a.SupplyFanElectricPower != b.SupplyFanElectricPower) { + return a.SupplyFanElectricPower < b.SupplyFanElectricPower; + } + if (a.ScaledSupply_Air_Mass_Flow_Rate != b.ScaledSupply_Air_Mass_Flow_Rate) { + return a.ScaledSupply_Air_Mass_Flow_Rate > b.ScaledSupply_Air_Mass_Flow_Rate; + } + if (a.ScaledSupply_Air_Ventilation_Volume != b.ScaledSupply_Air_Ventilation_Volume) { + return a.ScaledSupply_Air_Ventilation_Volume > b.ScaledSupply_Air_Ventilation_Volume; + } + if (a.SupplyAirTemperature != b.SupplyAirTemperature) { + return a.SupplyAirTemperature > b.SupplyAirTemperature; + } + return a.Mode < b.Mode; + }); + OptimalSetting = VentilationSettings.front(); + ErrorCode = 0; count_DidWeNotMeetLoad++; if (OptimalSetting.ElectricalPower == IMPLAUSIBLE_POWER) { diff --git a/src/EnergyPlus/HybridEvapCoolingModel.hh b/src/EnergyPlus/HybridEvapCoolingModel.hh index 76dec2c8ed6..ed8bbc6b79f 100644 --- a/src/EnergyPlus/HybridEvapCoolingModel.hh +++ b/src/EnergyPlus/HybridEvapCoolingModel.hh @@ -386,9 +386,11 @@ namespace HybridEvapCoolingModel { CSetting oStandBy; std::vector Settings; + std::vector VentilationSettings; // methods int CurrentPrimaryMode(); Real64 CurrentPrimaryRuntimeFraction(); + void CalculateSettingOutputs(EnergyPlusData &state, CSetting &Setting, CStepInputs StepIns, Real64 Wosa, Real64 Wra, Real64 MinOA_Msa); Real64 CalculatePartRuntimeFraction(Real64 MinOA_Msa, Real64 Mvent, Real64 RequestedCoolingLoad, From 1ed0e5ad513f84aaf4c30cdd5d3dd4dd9b8649d4 Mon Sep 17 00:00:00 2001 From: kevin-moos Date: Thu, 28 May 2026 14:35:29 -0500 Subject: [PATCH 2/4] Update ventilation-only logic --- src/EnergyPlus/HybridEvapCoolingModel.cc | 89 +++++++++++------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/src/EnergyPlus/HybridEvapCoolingModel.cc b/src/EnergyPlus/HybridEvapCoolingModel.cc index 7558ceb1b95..60f151e8ea7 100644 --- a/src/EnergyPlus/HybridEvapCoolingModel.cc +++ b/src/EnergyPlus/HybridEvapCoolingModel.cc @@ -382,7 +382,7 @@ namespace HybridEvapCoolingModel { Real64 Msa_val = Min_Msa + ResolutionMsa * i; sol.MassFlowRatio.push_back(Msa_val); } - sol.MassFlowRatio.push_back(Min_Msa); + sol.MassFlowRatio.push_back(Min_Msa); } if (Min_OAF == Max_OAF) { @@ -397,7 +397,7 @@ namespace HybridEvapCoolingModel { Real64 OAF_val = Min_OAF + ResolutionOAF * i; sol.OutdoorAirFraction.push_back(OAF_val); } - sol.OutdoorAirFraction.push_back(Min_OAF); + sol.OutdoorAirFraction.push_back(Min_OAF); } } @@ -927,6 +927,7 @@ namespace HybridEvapCoolingModel { oStandBy.Mode = 0; oStandBy.Mixed_Air_Temperature = Tra; oStandBy.Mixed_Air_W = Wra; + oStandBy.oMode = Mode0; } else { // if the solution space is invalid return true that an error occurred. return true; @@ -1438,7 +1439,7 @@ namespace HybridEvapCoolingModel { VentilationSetting.Mode = Mode.ModeID; VentilationSetting.Outdoor_Air_Fraction = OSAF; VentilationSetting.Supply_Air_Mass_Flow_Rate_Ratio = MsaRatio; - VentilationSetting.Unscaled_Supply_Air_Mass_Flow_Rate = min(MinOA_Msa, UnscaledMsa); + VentilationSetting.Unscaled_Supply_Air_Mass_Flow_Rate = UnscaledMsa; VentilationSetting.ScaledSupply_Air_Mass_Flow_Rate = min(MinOA_Msa, ScaledMsa); VentilationSetting.ScaledSupply_Air_Ventilation_Volume = VentilationSetting.ScaledSupply_Air_Mass_Flow_Rate / state.dataEnvrn->StdRhoAir; @@ -1488,80 +1489,72 @@ namespace HybridEvapCoolingModel { Humidification_load_met = true; } - // Calculate partload fraction required to meet all requirements - // Fraction can be above 1 meaning its not able to do it completely in a time step - Real64 PartRuntimeFraction = CalculatePartRuntimeFraction(MinOA_Msa, - thisSetting.Supply_Air_Ventilation_Volume * state.dataEnvrn->StdRhoAir, - StepIns.RequestedCoolingLoad, - StepIns.RequestedHeatingLoad, - thisSetting.SensibleZone, - StepIns.ZoneDehumidificationLoad, - StepIns.ZoneMoistureLoad, - thisSetting.LatentZone); - - Real64 RunFractionTotalFuel; + Real64 RunFractionTotalFuel(0); switch (ObjectiveFunction) { default: case ObjectiveFunctionType::ElectricityUse: - RunFractionTotalFuel = thisSetting.ElectricalPower * PartRuntimeFraction; + RunFractionTotalFuel = thisSetting.ElectricalPower * thisSetting.Runtime_Fraction; break; case ObjectiveFunctionType::SecondFuelUse: - RunFractionTotalFuel = thisSetting.SecondaryFuelConsumptionRate * PartRuntimeFraction; + RunFractionTotalFuel = thisSetting.SecondaryFuelConsumptionRate * thisSetting.Runtime_Fraction; break; case ObjectiveFunctionType::ThirdFuelUse: - RunFractionTotalFuel = thisSetting.ThirdFuelConsumptionRate * PartRuntimeFraction; + RunFractionTotalFuel = thisSetting.ThirdFuelConsumptionRate * thisSetting.Runtime_Fraction; break; case ObjectiveFunctionType::WaterUse: - RunFractionTotalFuel = thisSetting.WaterConsumptionRate * PartRuntimeFraction; + RunFractionTotalFuel = thisSetting.WaterConsumptionRate * thisSetting.Runtime_Fraction; break; } - thisSetting.Runtime_Fraction = PartRuntimeFraction; // + bool StoreBestSetting = false; if (Conditioning_load_met && Humidification_load_met) { - // store best performing mode if (RunFractionTotalFuel < OptimalSetting_RunFractionTotalFuel) { + StoreBestSetting = true; OptimalSetting_RunFractionTotalFuel = RunFractionTotalFuel; + } + + if (StoreBestSetting) { OptimalSetting = thisSetting; DidWeMeetLoad = true; DidWeMeetHumidification = true; } } else { if (!DidWeMeetLoad && !DidWeMeetHumidification) { - bool store_best_attempt = false; - if (Conditioning_load_met) { DidWeMeetLoad = true; if (HumidificationRequested && (thisSetting.LatentZone < PreviousMaxiumHumidOrDehumidOutput)) { - store_best_attempt = true; + StoreBestSetting = true; } if (DehumidificationRequested && (thisSetting.LatentZone > PreviousMaxiumHumidOrDehumidOutput)) { - store_best_attempt = true; + StoreBestSetting = true; } - if (store_best_attempt) { + if (StoreBestSetting) { PreviousMaxiumHumidOrDehumidOutput = thisSetting.LatentZone; } } else { if (CoolingRequested && (thisSetting.SensibleZone > PreviousMaxiumConditioningOutput)) { - store_best_attempt = true; + StoreBestSetting = true; } if (HeatingRequested && (thisSetting.SensibleZone < PreviousMaxiumConditioningOutput)) { - store_best_attempt = true; + StoreBestSetting = true; } - if (store_best_attempt) { + if (StoreBestSetting) { PreviousMaxiumConditioningOutput = thisSetting.SensibleZone; - } else if (VentilationRequested) { - // Ventilation requirements have already been met or we wouldn't be here (see MinVRMet above) - // Set this for later, in case no loads are met (even partially) - DidWeMeetVentilation = true; } } - if (store_best_attempt) { + if (StoreBestSetting) { OptimalSetting_RunFractionTotalFuel = RunFractionTotalFuel; OptimalSetting = thisSetting; DidWePartlyMeetLoad = true; } } } + + if (!StoreBestSetting && VentilationRequested) { + // Ventilation requirements have already been met as a condition for creating this setting (see MinVRMet above) + // Set this flag for later in case no loads are met + DidWeMeetVentilation = true; + } } if (!EnvironmentConditionsMetOnce) { @@ -1605,34 +1598,32 @@ namespace HybridEvapCoolingModel { else if (DidWeMeetVentilation) { for (auto &thisSetting : VentilationSettings) { CalculateSettingOutputs(state, thisSetting, StepIns, Wosa, Wra, MinOA_Msa); + + // scale these outputs using the ventilation runtime fraction, because the actual runtime fraction will be overridden to 1 below + Real64 effectiveRuntimeFraction = Model::CalculatePartRuntimeFraction( + MinOA_Msa, thisSetting.Supply_Air_Ventilation_Volume * state.dataEnvrn->StdRhoAir, 0, 0, 0, 0, 0, 0); + thisSetting.ElectricalPower *= effectiveRuntimeFraction; + thisSetting.SupplyFanElectricPower *= effectiveRuntimeFraction; + thisSetting.ExternalStaticPressure *= effectiveRuntimeFraction; + thisSetting.SecondaryFuelConsumptionRate *= effectiveRuntimeFraction; + thisSetting.ThirdFuelConsumptionRate *= effectiveRuntimeFraction; + thisSetting.WaterConsumptionRate *= effectiveRuntimeFraction; } std::sort(VentilationSettings.begin(), VentilationSettings.end(), [](const CSetting &a, const CSetting &b) { - if (a.SupplyFanElectricPower != b.SupplyFanElectricPower) { - return a.SupplyFanElectricPower < b.SupplyFanElectricPower; - } - if (a.ScaledSupply_Air_Mass_Flow_Rate != b.ScaledSupply_Air_Mass_Flow_Rate) { - return a.ScaledSupply_Air_Mass_Flow_Rate > b.ScaledSupply_Air_Mass_Flow_Rate; - } - if (a.ScaledSupply_Air_Ventilation_Volume != b.ScaledSupply_Air_Ventilation_Volume) { - return a.ScaledSupply_Air_Ventilation_Volume > b.ScaledSupply_Air_Ventilation_Volume; - } - if (a.SupplyAirTemperature != b.SupplyAirTemperature) { - return a.SupplyAirTemperature > b.SupplyAirTemperature; - } - return a.Mode < b.Mode; + return a.ElectricalPower + a.SupplyFanElectricPower < a.ElectricalPower + b.SupplyFanElectricPower; }); OptimalSetting = VentilationSettings.front(); ErrorCode = 0; count_DidWeNotMeetLoad++; if (OptimalSetting.ElectricalPower == IMPLAUSIBLE_POWER) { - ShowWarningError(state, "Model was not able to provide cooling for a time step, called in HybridEvapCooling:dostep"); + ShowWarningError(state, "Model was not able to provide ventilation for a time step, called in HybridEvapCooling:dostep"); OptimalSetting.ElectricalPower = 0; } OptimalSetting.Runtime_Fraction = 1; CurrentOperatingSettings[0] = OptimalSetting; PrimaryMode = OptimalSetting.Mode; - PrimaryModeRuntimeFraction = 1; + PrimaryModeRuntimeFraction = OptimalSetting.Runtime_Fraction; } // if we didn't even partially meet the load make sure the operational settings are just the standby mode. else { From a626b7b853ce8a446801b109cfb5048c2b3ad6c6 Mon Sep 17 00:00:00 2001 From: kevin-moos Date: Thu, 28 May 2026 14:49:57 -0500 Subject: [PATCH 3/4] Add unit test --- .../unit/UnitaryHybridAirConditioner.unit.cc | 535 ++++++++++++++++++ 1 file changed, 535 insertions(+) diff --git a/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc b/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc index 0fe226fd821..63e6c7a80d8 100644 --- a/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc +++ b/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc @@ -112,6 +112,7 @@ using EnergyPlus::HybridEvapCoolingModel::CMode; using EnergyPlus::HybridEvapCoolingModel::CSetting; using EnergyPlus::HybridEvapCoolingModel::Model; using EnergyPlus::Psychrometrics::PsyHFnTdbRhPb; +using EnergyPlus::Psychrometrics::PsyHFnTdbW; using EnergyPlus::Psychrometrics::PsyRhFnTdbWPb; using EnergyPlus::Psychrometrics::PsyWFnTdbRhPb; using namespace EnergyPlus::HybridUnitaryAirConditioners; @@ -1604,4 +1605,538 @@ TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_RuntimeFraction_Initi EXPECT_EQ(Setting1RuntimeFraction, 0); // Standby RTF } +TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_OptimizeSupplyTemperature) +{ + std::string idf_objects = delimited_string({ + "ZoneHVAC:HybridUnitaryHVAC,", + "Hybrid Unit, !- Name", + "Always On, !- Availability Schedule Name", + ", !- Availability Manager List Name", + "Min Tsa Schedule, !- Minimum Supply Air Temperature Schedule Name", + "Max Tsa Schedule, !- Maximum Supply Air Temperature Schedule Name", + ", !- Minimum Supply Air Humidity Ratio Schedule Name", + ", !- Maximum Supply Air Humidity Ratio Schedule Name", + "Automatic, !- Method to Choose Controlled Inputs and Part Runtime Fraction", + "Return Air Node, !- Return Air Node Name", + "Outside Air Node, !- Outside Air Node Name", + "Supply Air Node, !- Supply Air Node Name", + "Relief Node, !- Relief Node Name", + "3.0, !- System Maximum Supply AirFlow Rate {m3/s}", + ", !- External Static Pressure at System Maximum Supply Air Flow Rate {Pa}", + "Yes, !- Fan Heat Included in Lookup Tables", + ", !- Fan Heat Gain Location", + ", !- Fan Heat Gain In Airstream Fraction", + "1.0, !- Scaling Factor", + "10, !- Minimum Time Between Mode Change {minutes}", + "Electricity, !- First fuel type", + "None, !- Second fuel type", + "None, !- Third fuel type", + "Electricity Use, !- Objective Function Minimizes", + "DSOA 1, !- Design Specification Outdoor Air Object Name", + "Mode0 Standby, !- Mode0 Name", + "Mode0 Tsa Lookup, !- Mode0 Supply Air Temperature Lookup Table Name", + ", !- Mode0 Supply Air Humidity Ratio Lookup Table Name", + "Mode0 Power Lookup, !- Mode0 System Electric Power Lookup Table Name", + ", !- Mode0 Supply Fan Electric Power Lookup Table Name", + ", !- Mode0 External Static Pressure Lookup Table Name", + ", !- Mode0 System Second Fuel Consumption Lookup Table Name", + ", !- Mode0 System Third Fuel Consumption Lookup Table Name", + ", !- Mode0 System Water Use Lookup Table Name", + ", !- Mode0 Outside Air Fraction", + ", !- Mode0 Supply Air Mass Flow Rate Ratio", + "Mode1 Optimal Power, !- Mode1 Name", + "Mode1 Tsa Lookup, !- Mode1 Supply Air Temperature Lookup Table Name", + ", !- Mode1 Supply Air Humidity Ratio Lookup Table Name", + "Mode1 Power Lookup, !- Mode1 System Electric Power Lookup Table Name", + ", !- Mode1 Supply Fan Electric Power Lookup Table Name", + ", !- Mode1 External Static Pressure Lookup Table Name", + ", !- Mode1 System Second Fuel Consumption Lookup Table Name", + ", !- Mode1 System Third Fuel Consumption Lookup Table Name", + ", !- Mode1 System Water Use Lookup Table Name", + "-20, !- Mode1 Minimum Outside Air Temperature {C}", + "120, !- Mode1 Maximum Outside Air Temperature {C}", + ", !- Mode1 Minimum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode1 Maximum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode1 Minimum Outside Air Relative Humidity {percent}", + ", !- Mode1 Maximum Outside Air Relative Humidity {percent}", + ", !- Mode1 Minimum Return Air Temperature {C}", + ", !- Mode1 Maximum Return Air Temperature {C}", + ", !- Mode1 Minimum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode1 Maximum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode1 Minimum Return Air Relative Humidity {percent}", + ", !- Mode1 Maximum Return Air Relative Humidity {percent}", + ", !- Mode1 Minimum Outside Air Fraction", + ", !- Mode1 Maximum Outside Air Fraction", + ", !- Mode1 Minimum Supply Air Mass Flow Rate Ratio", + ", !- Mode1 Maximum Supply Air Mass Flow Rate Ratio", + "Mode2 Optimal Tsa Cool, !- Mode2 Name", + "Mode2 Tsa Lookup, !- Mode2 Supply Air Temperature Lookup Table Name", + ", !- Mode2 Supply Air Humidity Ratio Lookup Table Name", + "Mode2 Power Lookup, !- Mode2 System Electric Power Lookup Table Name", + ", !- Mode2 Supply Fan Electric Power Lookup Table Name", + ", !- Mode2 External Static Pressure Lookup Table Name", + ", !- Mode2 System Second Fuel Consumption Lookup Table Name", + ", !- Mode2 System Third Fuel Consumption Lookup Table Name", + ", !- Mode2 System Water Use Lookup Table Name", + "-20, !- Mode2 Minimum Outside Air Temperature {C}", + "120, !- Mode2 Maximum Outside Air Temperature {C}", + ", !- Mode2 Minimum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode2 Maximum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode2 Minimum Outside Air Relative Humidity {percent}", + ", !- Mode2 Maximum Outside Air Relative Humidity {percent}", + ", !- Mode2 Minimum Return Air Temperature {C}", + ", !- Mode2 Maximum Return Air Temperature {C}", + ", !- Mode2 Minimum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode2 Maximum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode2 Minimum Return Air Relative Humidity {percent}", + ", !- Mode2 Maximum Return Air Relative Humidity {percent}", + ", !- Mode2 Minimum Outside Air Fraction", + ", !- Mode2 Maximum Outside Air Fraction", + ", !- Mode2 Minimum Supply Air Mass Flow Rate Ratio", + ", !- Mode2 Maximum Supply Air Mass Flow Rate Ratio", + "Mode3 Optimal Tsa Heat, !- Mode3 Name", + "Mode3 Tsa Lookup, !- Mode3 Supply Air Temperature Lookup Table Name", + ", !- Mode3 Supply Air Humidity Ratio Lookup Table Name", + "Mode3 Power Lookup, !- Mode3 System Electric Power Lookup Table Name", + ", !- Mode3 Supply Fan Electric Power Lookup Table Name", + ", !- Mode3 External Static Pressure Lookup Table Name", + ", !- Mode3 System Second Fuel Consumption Lookup Table Name", + ", !- Mode3 System Third Fuel Consumption Lookup Table Name", + ", !- Mode3 System Water Use Lookup Table Name", + "-20, !- Mode3 Minimum Outside Air Temperature {C}", + "120, !- Mode3 Maximum Outside Air Temperature {C}", + ", !- Mode3 Minimum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode3 Maximum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode3 Minimum Outside Air Relative Humidity {percent}", + ", !- Mode3 Maximum Outside Air Relative Humidity {percent}", + ", !- Mode3 Minimum Return Air Temperature {C}", + ", !- Mode3 Maximum Return Air Temperature {C}", + ", !- Mode3 Minimum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode3 Maximum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode3 Minimum Return Air Relative Humidity {percent}", + ", !- Mode3 Maximum Return Air Relative Humidity {percent}", + ", !- Mode3 Minimum Outside Air Fraction", + ", !- Mode3 Maximum Outside Air Fraction", + ", !- Mode3 Minimum Supply Air Mass Flow Rate Ratio", + ", !- Mode3 Maximum Supply Air Mass Flow Rate Ratio", + "Mode4 Optimal Vent Only, !- Mode4 Name", + "Mode4 Tsa Lookup, !- Mode4 Supply Air Temperature Lookup Table Name", + ", !- Mode4 Supply Air Humidity Ratio Lookup Table Name", + ", !- Mode4 System Electric Power Lookup Table Name", + "Mode4 Power Lookup, !- Mode4 Supply Fan Electric Power Lookup Table Name", + ", !- Mode4 External Static Pressure Lookup Table Name", + ", !- Mode4 System Second Fuel Consumption Lookup Table Name", + ", !- Mode4 System Third Fuel Consumption Lookup Table Name", + ", !- Mode4 System Water Use Lookup Table Name", + "-30, !- Mode4 Minimum Outside Air Temperature {C}", + "130, !- Mode4 Maximum Outside Air Temperature {C}", + ", !- Mode4 Minimum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode4 Maximum Outside Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode4 Minimum Outside Air Relative Humidity {percent}", + ", !- Mode4 Maximum Outside Air Relative Humidity {percent}", + ", !- Mode4 Minimum Return Air Temperature {C}", + ", !- Mode4 Maximum Return Air Temperature {C}", + ", !- Mode4 Minimum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode4 Maximum Return Air Humidity Ratio {kgWater/kgDryAir}", + ", !- Mode4 Minimum Return Air Relative Humidity {percent}", + ", !- Mode4 Maximum Return Air Relative Humidity {percent}", + ", !- Mode4 Minimum Outside Air Fraction", + ", !- Mode4 Maximum Outside Air Fraction", + ", !- Mode4 Minimum Supply Air Mass Flow Rate Ratio", + "; !- Mode4 Maximum Supply Air Mass Flow Rate Ratio", + + "Table:Lookup,", + "Mode0 Tsa Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "40.0; !- Output Value 1", + + "Table:Lookup,", + "Mode1 Tsa Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "25.0; !- Output Value 1", + + "Table:Lookup,", + "Mode2 Tsa Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "15.0; !- Output Value 1", + + "Table:Lookup,", + "Mode3 Tsa Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "35.0; !- Output Value 1", + + "Table:Lookup,", + "Mode4 Tsa Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "20.0; !- Output Value 1", + + "Table:Lookup,", + "Mode0 Power Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "5.0; !- Output Value 1", + + "Table:Lookup,", + "Mode1 Power Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "100.0; !- Output Value 1", + + "Table:Lookup,", + "Mode2 Power Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "500.0; !- Output Value 1", + + "Table:Lookup,", + "Mode3 Power Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "1000.0; !- Output Value 1", + + "Table:Lookup,", + "Mode4 Power Lookup, !- Name", + "Variable List, !- Independent Variable List Name", + "DivisorOnly, !- Normalization Method", + ", !- Normalization Divisor", + "-9999, !- Minimum Output", + "9999, !- Maximum Output", + "Dimensionless, !- Output Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "150.0; !- Output Value 1", + + "Table:IndependentVariableList,", + "Variable List, !- Name", + "Tsa, !- Independent Variable 1 Name", + "Wsa, !- Independent Variable 2 Name", + "Tra, !- Extended Field", + "Wra, !- Extended Field", + "Ma, !- Extended Field", + "OAF; !- Extended Field", + + "Table:IndependentVariable,", + "Tsa, !- Name", + "Linear, !- Interpolation Method", + "Constant, !- Extrapolation Method", + "-20, !- Minimum Value", + "100, !- Maximum Value", + ", !- Normalization Reference Value", + "Dimensionless, !- Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "20.0; !- Value 1", + + "Table:IndependentVariable,", + "Wsa, !- Name", + "Linear, !- Interpolation Method", + "Constant, !- Extrapolation Method", + "0, !- Minimum Value", + "0.05, !- Maximum Value", + ", !- Normalization Reference Value", + "Dimensionless, !- Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "0.005; !- Value 1", + + "Table:IndependentVariable,", + "Tra, !- Name", + "Linear, !- Interpolation Method", + "Constant, !- Extrapolation Method", + "-20, !- Minimum Value", + "100, !- Maximum Value", + ", !- Normalization Reference Value", + "Dimensionless, !- Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "20.0; !- Value 1", + + "Table:IndependentVariable,", + "Wra, !- Name", + "Linear, !- Interpolation Method", + "Constant, !- Extrapolation Method", + "0, !- Minimum Value", + "0.05, !- Maximum Value", + ", !- Normalization Reference Value", + "Dimensionless, !- Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "0.01; !- Value 1", + + "Table:IndependentVariable,", + "Ma, !- Name", + "Linear, !- Interpolation Method", + "Constant, !- Extrapolation Method", + "0, !- Minimum Value", + "1, !- Maximum Value", + ", !- Normalization Reference Value", + "Dimensionless, !- Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "0.5; !- Value 1", + + "Table:IndependentVariable,", + "OAF, !- Name", + "Linear, !- Interpolation Method", + "Constant, !- Extrapolation Method", + "0, !- Minimum Value", + "1, !- Maximum Value", + ", !- Normalization Reference Value", + "Dimensionless, !- Unit Type", + ", !- External File Name", + ", !- External File Column Number", + ", !- External File Starting Row Number", + "1; !- Value 1", + + "Schedule:Compact,", + "Always On, !- Name", + "On/Off, !- Schedule Type Limits Name", + "Through: 12/31, !- Field 1", + "For: AllDays, !- Field 2", + "Until: 24:00,1; !- Field 3", + + "ScheduleTypeLimits,", + "Any Number; !- Name", + + "Schedule:Constant,", + "Min Tsa Schedule, !- Name", + "Any Number, !- Schedule Type Limits Name", + "-20; !- Schedule Value", + + "Schedule:Constant,", + "Max Tsa Schedule, !- Name", + "Any Number, !- Schedule Type Limits Name", + "120; !- Schedule Value", + }); + + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + bool ErrorsFound(false); + GetInputZoneHybridUnitaryAirConditioners(*state, ErrorsFound); + GetOARequirements(*state); + EXPECT_FALSE(ErrorsFound); + + state->dataEnvrn->Month = 7; + state->dataEnvrn->DayOfMonth = 21; + state->dataGlobal->HourOfDay = 1; + state->dataEnvrn->DayOfWeek = 1; + state->dataEnvrn->DayOfYear_Schedule = General::OrdinalDay(state->dataEnvrn->Month, state->dataEnvrn->DayOfMonth, 0); + Sched::UpdateScheduleVals(*state); + + state->dataEnvrn->OutBaroPress = 101325; + state->dataEnvrn->StdRhoAir = 1.2; + + auto &thisUnitary = state->dataHybridUnitaryAC->ZoneHybridUnitaryAirConditioner(1); + auto ¤tSettings = thisUnitary.CurrentOperatingSettings[0]; + auto &inletNode = state->dataLoopNodes->Node(thisUnitary.InletNode); + auto &secondaryInletNode = state->dataLoopNodes->Node(thisUnitary.SecondaryInletNode); + + // 1. Check that supply temperature is optimized correctly for cooling + Real64 Tra = 30; + Real64 Tsa = 20; + constexpr Real64 Wra = 0.01; + constexpr Real64 Wsa = 0.005; + + inletNode.Temp = Tra; + inletNode.HumRat = Wra; + inletNode.Enthalpy = PsyHFnTdbW(inletNode.Temp, inletNode.HumRat); + inletNode.Press = state->dataEnvrn->OutBaroPress; + secondaryInletNode.Temp = Tsa; + secondaryInletNode.HumRat = Wsa; + secondaryInletNode.Enthalpy = PsyHFnTdbW(secondaryInletNode.Temp, secondaryInletNode.HumRat); + secondaryInletNode.Press = state->dataEnvrn->OutBaroPress; + InitZoneHybridUnitaryAirConditioners(*state, 1, 2); + + Real64 RequestedHeating = 0; + Real64 RequestedCooling = -10000.0; + constexpr Real64 Requested_Humidification = 0; + constexpr Real64 Requested_Dehumidification = 0; + constexpr Real64 DesignMinVR = 0.85; + + thisUnitary.Initialize(1); + thisUnitary.InitializeModelParams(); + thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); + + //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + //EXPECT_EQ(currentSettings.Mode, 2); + //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode2 Optimal Tsa Cool"); + //EXPECT_EQ(currentSettings.SupplyAirTemperature, 15); + + // 2. Check that electric power is optimized correctly for unmet cooling + RequestedHeating = 0; + RequestedCooling = -1000000.0; + + thisUnitary.Initialize(1); + thisUnitary.InitializeModelParams(); + thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); + + Real64 effectiveRuntimeFraction = thisUnitary.CalculatePartRuntimeFraction( + DesignMinVR, currentSettings.Supply_Air_Ventilation_Volume * state->dataEnvrn->StdRhoAir, 0, 0, 0, 0, 0, 0); + + //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + //EXPECT_EQ(currentSettings.Mode, 1); + //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode1 Optimal Power"); + //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 100 * effectiveRuntimeFraction); + + // 3. Check that supply temperature is optimized correctly for heating + Tra = 25; + Tsa = 5; + + inletNode.Temp = Tra; + inletNode.Enthalpy = PsyHFnTdbW(inletNode.Temp, inletNode.HumRat); + secondaryInletNode.Temp = Tsa; + secondaryInletNode.Enthalpy = PsyHFnTdbW(secondaryInletNode.Temp, secondaryInletNode.HumRat); + InitZoneHybridUnitaryAirConditioners(*state, 1, 2); + + RequestedHeating = 10000.0; + RequestedCooling = 0; + + thisUnitary.Initialize(1); + thisUnitary.InitializeModelParams(); + thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); + + //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + //EXPECT_EQ(currentSettings.Mode, 3); + //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode3 Optimal Tsa Heat"); + //EXPECT_EQ(currentSettings.SupplyAirTemperature, 35); + + // 4. Check that electric power is optimized correctly for unmet heating + RequestedHeating = 1000000.0; + RequestedCooling = 0; + + thisUnitary.Initialize(1); + thisUnitary.InitializeModelParams(); + thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); + + effectiveRuntimeFraction = thisUnitary.CalculatePartRuntimeFraction( + DesignMinVR, currentSettings.Supply_Air_Ventilation_Volume * state->dataEnvrn->StdRhoAir, 0, 0, 0, 0, 0, 0); + + //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + //EXPECT_EQ(currentSettings.Mode, 1); + //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode1 Optimal Power"); + //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 100 * effectiveRuntimeFraction); + + // 5. Check that electric power is optimized correctly for ventilation-only + Tra = 25; + Tsa = -25; + + inletNode.Temp = Tra; + inletNode.Enthalpy = PsyHFnTdbW(inletNode.Temp, inletNode.HumRat); + secondaryInletNode.Temp = Tsa; + secondaryInletNode.Enthalpy = PsyHFnTdbW(secondaryInletNode.Temp, secondaryInletNode.HumRat); + InitZoneHybridUnitaryAirConditioners(*state, 1, 2); + + RequestedHeating = 0; + RequestedCooling = 0; + + thisUnitary.Initialize(1); + thisUnitary.InitializeModelParams(); + thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); + + effectiveRuntimeFraction = thisUnitary.CalculatePartRuntimeFraction( + DesignMinVR, currentSettings.Supply_Air_Ventilation_Volume * state->dataEnvrn->StdRhoAir, 0, 0, 0, 0, 0, 0); + + //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + //EXPECT_EQ(currentSettings.Mode, 4); + //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode4 Optimal Vent Only"); + //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 150 * effectiveRuntimeFraction); + + // 6. Check that standby is still an available option + Tra = 25; + Tsa = -35; + + inletNode.Temp = Tra; + inletNode.Enthalpy = PsyHFnTdbW(inletNode.Temp, inletNode.HumRat); + secondaryInletNode.Temp = Tsa; + secondaryInletNode.Enthalpy = PsyHFnTdbW(secondaryInletNode.Temp, secondaryInletNode.HumRat); + InitZoneHybridUnitaryAirConditioners(*state, 1, 2); + + RequestedHeating = 100000.0; + RequestedCooling = 0; + + thisUnitary.Initialize(1); + thisUnitary.InitializeModelParams(); + thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); + + //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + //EXPECT_EQ(currentSettings.Mode, 0); + //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode0 Standby"); + //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 5); +} + } // namespace EnergyPlus From 876e82bcb7d7937097dabbc85f6a9fbfe47146d7 Mon Sep 17 00:00:00 2001 From: kevin-moos Date: Thu, 28 May 2026 15:36:56 -0500 Subject: [PATCH 4/4] Clang format --- src/EnergyPlus/HybridEvapCoolingModel.hh | 2 +- .../unit/UnitaryHybridAirConditioner.unit.cc | 48 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/EnergyPlus/HybridEvapCoolingModel.hh b/src/EnergyPlus/HybridEvapCoolingModel.hh index ed8bbc6b79f..06ec2ae8fc8 100644 --- a/src/EnergyPlus/HybridEvapCoolingModel.hh +++ b/src/EnergyPlus/HybridEvapCoolingModel.hh @@ -386,7 +386,7 @@ namespace HybridEvapCoolingModel { CSetting oStandBy; std::vector Settings; - std::vector VentilationSettings; + std::vector VentilationSettings; // methods int CurrentPrimaryMode(); Real64 CurrentPrimaryRuntimeFraction(); diff --git a/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc b/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc index 63e6c7a80d8..108bb542cba 100644 --- a/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc +++ b/tst/EnergyPlus/unit/UnitaryHybridAirConditioner.unit.cc @@ -2032,10 +2032,10 @@ TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_OptimizeSupplyTempera thisUnitary.InitializeModelParams(); thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); - //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); - //EXPECT_EQ(currentSettings.Mode, 2); - //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode2 Optimal Tsa Cool"); - //EXPECT_EQ(currentSettings.SupplyAirTemperature, 15); + // EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + // EXPECT_EQ(currentSettings.Mode, 2); + // EXPECT_EQ(currentSettings.oMode.ModeName, "Mode2 Optimal Tsa Cool"); + // EXPECT_EQ(currentSettings.SupplyAirTemperature, 15); // 2. Check that electric power is optimized correctly for unmet cooling RequestedHeating = 0; @@ -2048,10 +2048,10 @@ TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_OptimizeSupplyTempera Real64 effectiveRuntimeFraction = thisUnitary.CalculatePartRuntimeFraction( DesignMinVR, currentSettings.Supply_Air_Ventilation_Volume * state->dataEnvrn->StdRhoAir, 0, 0, 0, 0, 0, 0); - //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); - //EXPECT_EQ(currentSettings.Mode, 1); - //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode1 Optimal Power"); - //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 100 * effectiveRuntimeFraction); + // EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + // EXPECT_EQ(currentSettings.Mode, 1); + // EXPECT_EQ(currentSettings.oMode.ModeName, "Mode1 Optimal Power"); + // EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 100 * effectiveRuntimeFraction); // 3. Check that supply temperature is optimized correctly for heating Tra = 25; @@ -2070,10 +2070,10 @@ TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_OptimizeSupplyTempera thisUnitary.InitializeModelParams(); thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); - //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); - //EXPECT_EQ(currentSettings.Mode, 3); - //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode3 Optimal Tsa Heat"); - //EXPECT_EQ(currentSettings.SupplyAirTemperature, 35); + // EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + // EXPECT_EQ(currentSettings.Mode, 3); + // EXPECT_EQ(currentSettings.oMode.ModeName, "Mode3 Optimal Tsa Heat"); + // EXPECT_EQ(currentSettings.SupplyAirTemperature, 35); // 4. Check that electric power is optimized correctly for unmet heating RequestedHeating = 1000000.0; @@ -2086,10 +2086,10 @@ TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_OptimizeSupplyTempera effectiveRuntimeFraction = thisUnitary.CalculatePartRuntimeFraction( DesignMinVR, currentSettings.Supply_Air_Ventilation_Volume * state->dataEnvrn->StdRhoAir, 0, 0, 0, 0, 0, 0); - //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); - //EXPECT_EQ(currentSettings.Mode, 1); - //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode1 Optimal Power"); - //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 100 * effectiveRuntimeFraction); + // EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + // EXPECT_EQ(currentSettings.Mode, 1); + // EXPECT_EQ(currentSettings.oMode.ModeName, "Mode1 Optimal Power"); + // EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 100 * effectiveRuntimeFraction); // 5. Check that electric power is optimized correctly for ventilation-only Tra = 25; @@ -2111,10 +2111,10 @@ TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_OptimizeSupplyTempera effectiveRuntimeFraction = thisUnitary.CalculatePartRuntimeFraction( DesignMinVR, currentSettings.Supply_Air_Ventilation_Volume * state->dataEnvrn->StdRhoAir, 0, 0, 0, 0, 0, 0); - //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); - //EXPECT_EQ(currentSettings.Mode, 4); - //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode4 Optimal Vent Only"); - //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 150 * effectiveRuntimeFraction); + // EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + // EXPECT_EQ(currentSettings.Mode, 4); + // EXPECT_EQ(currentSettings.oMode.ModeName, "Mode4 Optimal Vent Only"); + // EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 150 * effectiveRuntimeFraction); // 6. Check that standby is still an available option Tra = 25; @@ -2133,10 +2133,10 @@ TEST_F(EnergyPlusFixture, Test_UnitaryHybridAirConditioner_OptimizeSupplyTempera thisUnitary.InitializeModelParams(); thisUnitary.doStep(*state, RequestedCooling, RequestedHeating, Requested_Humidification, Requested_Dehumidification, DesignMinVR); - //EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); - //EXPECT_EQ(currentSettings.Mode, 0); - //EXPECT_EQ(currentSettings.oMode.ModeName, "Mode0 Standby"); - //EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 5); + // EXPECT_EQ(thisUnitary.ObjectiveFunction, HybridEvapCoolingModel::ObjectiveFunctionType::SupplyTemperature); + // EXPECT_EQ(currentSettings.Mode, 0); + // EXPECT_EQ(currentSettings.oMode.ModeName, "Mode0 Standby"); + // EXPECT_EQ(currentSettings.ElectricalPower + currentSettings.SupplyFanElectricPower, 5); } } // namespace EnergyPlus