Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various DX coil fixes #8576

Merged
merged 11 commits into from
Mar 22, 2021
59 changes: 25 additions & 34 deletions src/EnergyPlus/DXCoils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -614,8 +614,8 @@ namespace EnergyPlus::DXCoils {

// Determine combined performance
state.dataDXCoils->DXCoil(DXCoilNum).OutletAirEnthalpy =
(1.0 - S12RuntimeFraction) * S1OutletAirEnthalpy + S12RuntimeFraction * S12OutletAirEnthalpy;
state.dataDXCoils->DXCoil(DXCoilNum).OutletAirHumRat = (1.0 - S12RuntimeFraction) * S1OutletAirHumRat + S12RuntimeFraction * S12OutletAirHumRat;
(1.0 - S2PLR) * S1OutletAirEnthalpy + S2PLR * S12OutletAirEnthalpy;
state.dataDXCoils->DXCoil(DXCoilNum).OutletAirHumRat = (1.0 - S2PLR) * S1OutletAirHumRat + S2PLR * S12OutletAirHumRat;
Comment on lines -617 to +618
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use PLR, not RuntimeFraction to calculate leaving conditions for 2-stage operation (Coil:Cooling:DX:TwoStageWithHumidityControlMode). RuntimeFraction is appropriate for power consumption.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

state.dataDXCoils->DXCoil(DXCoilNum).OutletAirTemp = PsyTdbFnHW(state.dataDXCoils->DXCoil(DXCoilNum).OutletAirEnthalpy, state.dataDXCoils->DXCoil(DXCoilNum).OutletAirHumRat);
// Check for saturation error and modify temperature at constant enthalpy
if (state.dataDXCoils->DXCoil(DXCoilNum).CondenserInletNodeNum(PerfMode) != 0) {
Expand Down Expand Up @@ -11822,7 +11822,6 @@ namespace EnergyPlus::DXCoils {
Real64 LSElecCoolingPower; // low speed power [W]
Real64 HSElecCoolingPower; // high speed power [W]
Real64 CrankcaseHeatingPower; // Power due to crank case heater
Real64 Hfg;
Real64 AirVolumeFlowRate; // Air volume flow rate across the heating coil
Real64 VolFlowperRatedTotCap; // Air volume flow rate divided by rated total heating capacity

Expand Down Expand Up @@ -12193,14 +12192,9 @@ namespace EnergyPlus::DXCoils {
OutletAirHumRat = HSOutletAirHumRat;
OutletAirDryBulbTemp = HSOutletAirDryBulbTemp;
} else {
if (FanOpMode == ContFanCycCoil) {
Real64 MinAirHumRat(0.0); // set to zero because MinAirHumRat is unused argument
Hfg = PsyHfgAirFnWTdb(MinAirHumRat, HSOutletAirDryBulbTemp * SpeedRatio + (1.0 - SpeedRatio) * LSOutletAirDryBulbTemp);
// Average outlet HR
OutletAirHumRat = InletAirHumRat - state.dataDXCoils->DXCoil(DXCoilNum).LatCoolingEnergyRate / Hfg / state.dataDXCoils->DXCoil(DXCoilNum).InletAirMassFlowRate;
} else {
OutletAirHumRat = (HSOutletAirHumRat * SpeedRatio) + (LSOutletAirHumRat * (1.0 - SpeedRatio));
}
OutletAirHumRat =
((HSOutletAirHumRat * SpeedRatio * MSHPMassFlowRateHigh) + (LSOutletAirHumRat * (1.0 - SpeedRatio) * MSHPMassFlowRateLow)) /
state.dataDXCoils->DXCoil(DXCoilNum).InletAirMassFlowRate;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multispeed DX fix massflow weighting between speeds (2398ce6)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall this from @lgu1234 a while back.

OutletAirDryBulbTemp = PsyTdbFnHW(OutletAirEnthalpy, OutletAirHumRat);
if (OutletAirDryBulbTemp < OutletAirDryBulbTempSat) { // Limit to saturated conditions at OutletAirEnthalpy
OutletAirDryBulbTemp = OutletAirDryBulbTempSat;
Expand All @@ -12226,6 +12220,9 @@ namespace EnergyPlus::DXCoils {
state.dataDXCoils->DXCoil(DXCoilNum).ElecCoolingPower = state.dataDXCoils->DXCoil(DXCoilNum).CoolingCoilRuntimeFraction * HSElecCoolingPower +
(1.0 - state.dataDXCoils->DXCoil(DXCoilNum).CoolingCoilRuntimeFraction) * LSElecCoolingPower;
}
// Now reset runtime fraction to 1.0 (because LS is running the full timestep)
state.dataDXCoils->DXCoil(DXCoilNum).CoolingCoilRuntimeFraction = 1.0;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This inside a big if (SpeedNum > 1 && SingleMode == 0) { so this means it running at speed 2 or higher. (bd8aa38)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.


// Calculation for heat reclaim needs to be corrected to use compressor power (not including condenser fan power)
state.dataHeatBal->HeatReclaimDXCoil(DXCoilNum).AvailCapacity = state.dataDXCoils->DXCoil(DXCoilNum).TotalCoolingEnergyRate + state.dataDXCoils->DXCoil(DXCoilNum).ElecCoolingPower;

Expand Down Expand Up @@ -12412,10 +12409,22 @@ namespace EnergyPlus::DXCoils {
}

if (FanOpMode == CycFanCycCoil) OnOffFanPartLoadFraction = PLF;
// outlet conditions are average of inlet and low speed weighted by CycRatio
OutletAirEnthalpy = LSOutletAirEnthalpy;
OutletAirHumRat = LSOutletAirHumRat;
OutletAirDryBulbTemp = LSOutletAirDryBulbTemp;
if (FanOpMode == ContFanCycCoil) {
// outlet conditions are average of inlet and low speed weighted by CycRatio
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multispeed DX fix outlet conditions at low speed (bd8aa38)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see in here where OutletAir* variables are calculated. Oh, there it is, in the else they are also set for cycling fan. OK then.

// Continuous fan, cycling compressor
Real64 CycAirFlowRatio =
CycRatio * AirMassFlow /
state.dataDXCoils->DXCoil(DXCoilNum).InletAirMassFlowRate; // ratio of compressor on airflow to average timestep airflow
OutletAirEnthalpy =
CycAirFlowRatio * LSOutletAirEnthalpy + (1.0 - CycAirFlowRatio) * InletAirEnthalpy;
OutletAirHumRat =
CycAirFlowRatio * LSOutletAirHumRat + (1.0 - CycAirFlowRatio) * InletAirHumRat;
OutletAirDryBulbTemp = PsyTdbFnHW(OutletAirEnthalpy, OutletAirHumRat);
} else {
OutletAirHumRat = LSOutletAirHumRat;
OutletAirDryBulbTemp = LSOutletAirDryBulbTemp;
OutletAirEnthalpy = LSOutletAirEnthalpy;
}
// get low speed EIR at current conditions
EIRTempModFacLS = CurveValue(state, state.dataDXCoils->DXCoil(DXCoilNum).MSEIRFTemp(SpeedNum), InletAirWetBulbC, CondInletTemp);
EIRFlowModFacLS = CurveValue(state, state.dataDXCoils->DXCoil(DXCoilNum).MSEIRFFlow(SpeedNum), AirMassFlowRatioLS);
Expand All @@ -12436,25 +12445,7 @@ namespace EnergyPlus::DXCoils {
state.dataDXCoils->DXCoil(DXCoilNum).TotalCoolingEnergyRate = state.dataDXCoils->DXCoil(DXCoilNum).TotalCoolingEnergyRate * CycRatio;
state.dataDXCoils->DXCoil(DXCoilNum).SensCoolingEnergyRate = state.dataDXCoils->DXCoil(DXCoilNum).SensCoolingEnergyRate * CycRatio;
state.dataDXCoils->DXCoil(DXCoilNum).LatCoolingEnergyRate = state.dataDXCoils->DXCoil(DXCoilNum).LatCoolingEnergyRate * CycRatio;
if (FanOpMode == ContFanCycCoil) {
OutletAirEnthalpy = InletAirEnthalpy - state.dataDXCoils->DXCoil(DXCoilNum).TotalCoolingEnergyRate / state.dataDXCoils->DXCoil(DXCoilNum).InletAirMassFlowRate;
Real64 MinAirHumRat(0.0); // set to zero because MinAirHumRat is unused argument
Hfg = PsyHfgAirFnWTdb(MinAirHumRat, OutletAirDryBulbTemp * CycRatio + (1.0 - CycRatio) * InletAirDryBulbTemp);
OutletAirHumRat = InletAirHumRat - state.dataDXCoils->DXCoil(DXCoilNum).LatCoolingEnergyRate / Hfg / state.dataDXCoils->DXCoil(DXCoilNum).InletAirMassFlowRate;
OutletAirDryBulbTemp = PsyTdbFnHW(OutletAirEnthalpy, OutletAirHumRat);
if (OutletAirDryBulbTemp < OutletAirDryBulbTempSat) { // Limit to saturated conditions at OutletAirEnthalpy
OutletAirDryBulbTemp = OutletAirDryBulbTempSat;
OutletAirHumRat = PsyWFnTdbH(state, OutletAirDryBulbTemp, OutletAirEnthalpy, RoutineName);
CalcComponentSensibleLatentOutput(state.dataDXCoils->DXCoil(DXCoilNum).InletAirMassFlowRate,
InletAirDryBulbTemp,
InletAirHumRat,
OutletAirDryBulbTemp,
OutletAirHumRat,
state.dataDXCoils->DXCoil(DXCoilNum).SensCoolingEnergyRate,
state.dataDXCoils->DXCoil(DXCoilNum).LatCoolingEnergyRate,
state.dataDXCoils->DXCoil(DXCoilNum).TotalCoolingEnergyRate);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above deleted code looks like it's catching up to develop.

// Calculation for heat reclaim needs to be corrected to use compressor power (not including condenser fan power)
state.dataHeatBal->HeatReclaimDXCoil(DXCoilNum).AvailCapacity = state.dataDXCoils->DXCoil(DXCoilNum).TotalCoolingEnergyRate + state.dataDXCoils->DXCoil(DXCoilNum).ElecCoolingPower;
state.dataDXCoils->DXCoil(DXCoilNum).OutletAirEnthalpy = OutletAirEnthalpy;
Expand Down
60 changes: 46 additions & 14 deletions src/EnergyPlus/UnitarySystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2140,7 +2140,8 @@ namespace UnitarySystems {
this->m_IdleSpeedRatio = this->m_IdleVolumeAirRate / this->m_DesignFanVolFlowRate;
}

} else if (this->m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_MultiSpeedCooling) {
} else if (this->m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_MultiSpeedCooling ||
this->m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_CoolingTwoSpeed) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow 2-speed dx to have variable speeds (ea02126)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually missing from develop? And now things line up better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rraustad You actually made this commit a year ago. ea02126 Ring a bell?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but at least I'm consistent. And that whole conversation of what does idle speed anything mean is still a little confusing. I see now there is a call to DXCoils::SimDXCoilMultiSpeed. That is the correct call for 2-spd coil? I've lost track but thought is was a different function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's fine. There are further checks on coil type in that function.
line 343 if (SELECT_CASE_var == CoilDX_CoolingTwoSpeed) {


if (this->m_NumOfSpeedCooling > 0) {
if (this->m_CoolVolumeFlowRate.size() == 0) this->m_CoolVolumeFlowRate.resize(this->m_NumOfSpeedCooling + 1);
Expand Down Expand Up @@ -2179,6 +2180,15 @@ namespace UnitarySystems {
this->m_IdleVolumeAirRate = this->m_MaxNoCoolHeatAirVolFlow;
this->m_IdleMassFlowRate = this->MaxNoCoolHeatAirMassFlow;
this->m_IdleSpeedRatio = this->m_IdleVolumeAirRate / this->m_DesignFanVolFlowRate;
} else {
for ( Iter = this->m_NumOfSpeedCooling; Iter > 0; --Iter ) {
this->m_CoolVolumeFlowRate[ Iter ] = this->m_MaxCoolAirVolFlow * Iter / this->m_NumOfSpeedCooling;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More allow 2-speed dx to be variable speed (ea02126)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this goes with the 2-speed coil addition to this section.

this->m_CoolMassFlowRate[ Iter ] = this->m_CoolVolumeFlowRate[ Iter ] * state.dataEnvrn->StdRhoAir;
this->m_MSCoolingSpeedRatio[ Iter ] = this->m_CoolVolumeFlowRate[ Iter ] / this->m_DesignFanVolFlowRate;
}
this->m_IdleVolumeAirRate = this->m_MaxNoCoolHeatAirVolFlow;
this->m_IdleMassFlowRate = this->m_IdleVolumeAirRate * state.dataEnvrn->StdRhoAir;
this->m_IdleSpeedRatio = this->m_IdleVolumeAirRate / this->m_DesignFanVolFlowRate;
}
} else if (this->m_CoolingCoilType_Num == DataHVACGlobals::Coil_CoolingWater ||
this->m_CoolingCoilType_Num == DataHVACGlobals::Coil_CoolingWaterDetailed) {
Expand Down Expand Up @@ -4414,7 +4424,13 @@ namespace UnitarySystems {

} else { // mine data from DX cooling coil

if (thisSys.m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_CoolingTwoSpeed) thisSys.m_NumOfSpeedCooling = 2;
if (thisSys.m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_CoolingTwoSpeed) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More allow 2-speed dx to be variable speed (ea02126)

thisSys.m_NumOfSpeedCooling = 2;
thisSys.m_MultiOrVarSpeedCoolCoil = true;
} else {
thisSys.m_NumOfSpeedCooling = 1;
thisSys.m_MultiOrVarSpeedCoolCoil = false;
}

// Get DX cooling coil index
DXCoils::GetDXCoilIndex(state,
Expand Down Expand Up @@ -7186,21 +7202,33 @@ namespace UnitarySystems {
"Average",
state.dataUnitarySystems->unitarySys[sysNum].Name);

SetupOutputVariable(state, "Unitary System Cooling Ancillary Electricity Energy",
OutputProcessor::Unit::J,
state.dataUnitarySystems->unitarySys[sysNum].m_CoolingAuxElecConsumption,
"System",
"Sum",
state.dataUnitarySystems->unitarySys[sysNum].Name,
_,
"Electricity",
"Cooling",
_,
"System");
SetupOutputVariable(state, "Unitary System Electricity Rate",
OutputProcessor::Unit::W,
state.dataUnitarySystems->unitarySys[sysNum].m_ElecPower,
"System",
"Average",
state.dataUnitarySystems->unitarySys[sysNum].Name);
SetupOutputVariable(state, "Unitary System Electricity Energy",
OutputProcessor::Unit::J,
state.dataUnitarySystems->unitarySys[sysNum].m_ElecPowerConsumption,
"System",
"Sum",
state.dataUnitarySystems->unitarySys[sysNum].Name);

{
auto const SELECT_CASE_var(state.dataUnitarySystems->unitarySys[sysNum].m_CoolingCoilType_Num);
if (SELECT_CASE_var == DataHVACGlobals::CoilDX_CoolingTwoSpeed) {
SetupOutputVariable(state, "Unitary System Cycling Ratio",
OutputProcessor::Unit::None,
state.dataUnitarySystems->unitarySys[sysNum].m_CycRatio,
"System",
"Average",
state.dataUnitarySystems->unitarySys[sysNum].Name);
SetupOutputVariable(state, "Unitary System Compressor Speed Ratio",
OutputProcessor::Unit::None,
state.dataUnitarySystems->unitarySys[sysNum].m_SpeedRatio,
"System",
"Average",
state.dataUnitarySystems->unitarySys[sysNum].Name);
} else if (SELECT_CASE_var == DataHVACGlobals::CoilDX_MultiSpeedCooling || (SELECT_CASE_var == DataHVACGlobals::CoilDX_Cooling)) {
if (state.dataUnitarySystems->unitarySys[sysNum].m_HeatRecActive) {
SetupOutputVariable(state, "Unitary System Heat Recovery Rate",
Expand Down Expand Up @@ -7286,6 +7314,7 @@ namespace UnitarySystems {
}

if (state.dataUnitarySystems->unitarySys[sysNum].m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_MultiSpeedCooling ||
state.dataUnitarySystems->unitarySys[sysNum].m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_CoolingTwoSpeed ||
state.dataUnitarySystems->unitarySys[sysNum].m_CoolingCoilType_Num == DataHVACGlobals::CoilDX_Cooling ||
state.dataUnitarySystems->unitarySys[sysNum].m_HeatingCoilType_Num == DataHVACGlobals::CoilDX_MultiSpeedHeating ||
state.dataUnitarySystems->unitarySys[sysNum].m_HeatingCoilType_Num == DataHVACGlobals::Coil_HeatingElectric_MultiStage ||
Expand Down Expand Up @@ -9077,7 +9106,10 @@ namespace UnitarySystems {
// RETURN if the moisture load is met
if (state.dataUnitarySystems->MoistureLoad >= 0.0 || state.dataUnitarySystems->MoistureLoad >= TempLatOutput) return;
// Multimode does not meet the latent load, only the sensible load with or without HX active
// what if there is a heating load for a system using Multimode?
if (!state.dataUnitarySystems->CoolingLoad && this->m_DehumidControlType_Num == DehumCtrlType::Multimode) return;
// if HX was previously turned on return since sensible load is already met
if (state.dataUnitarySystems->CoolingLoad && this->m_DehumidControlType_Num == DehumCtrlType::Multimode && HXUnitOn) return;
// IF(HeatingLoad .AND. UnitarySystem(UnitarySysNum)%m_DehumidControlType_Num .EQ. dehumidm_ControlType::CoolReheat)RETURN

if ((this->m_DehumidControlType_Num == DehumCtrlType::CoolReheat || this->m_DehumidControlType_Num == DehumCtrlType::Multimode)) {
Expand Down
24 changes: 12 additions & 12 deletions tst/EnergyPlus/unit/HVACMultiSpeedHeatPump.unit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1347,34 +1347,34 @@ TEST_F(EnergyPlusFixture, HVACMultiSpeedHeatPump_ReportVariableInitTest)
// Check outlet conditions
EXPECT_NEAR(DataLoopNode::Node(22).Temp, 23.363295, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).HumRat, 0.00796611, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 43744.6339, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.123500, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 43748.243, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.1232, 0.0001);

// Direct solution
state->dataGlobal->DoCoilDirectSolutions = true;
MSHeatPump(2).FullOutput.allocate(2);
SimMSHP(*state, MSHeatPumpNum, FirstHVACIteration, AirLoopNum, QSensUnitOut, QZnReq, OnOffAirFlowRatio);
// Check outlet conditions
EXPECT_NEAR(DataLoopNode::Node(22).Temp, 23.3641, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).HumRat, 0.00796613, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 43745.5237, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.123430, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Temp, 23.363295, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).HumRat, 0.00796611, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 43748.243, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.1232, 0.0001);

QZnReq = -10000.00;

state->dataGlobal->DoCoilDirectSolutions = false;
SimMSHP(*state, MSHeatPumpNum, FirstHVACIteration, AirLoopNum, QSensUnitOut, QZnReq, OnOffAirFlowRatio);
EXPECT_NEAR(DataLoopNode::Node(22).Temp, 21.4545, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Temp, 21.45298, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).HumRat, 0.00792169, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 41684.8507, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.285914, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 41691.15, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.285417, 0.0001);

state->dataGlobal->DoCoilDirectSolutions = true;
SimMSHP(*state, MSHeatPumpNum, FirstHVACIteration, AirLoopNum, QSensUnitOut, QZnReq, OnOffAirFlowRatio);
EXPECT_NEAR(DataLoopNode::Node(22).Temp, 21.4545, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Temp, 21.45298, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).HumRat, 0.00792169, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 41684.8507, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.285914, 0.0001);
EXPECT_NEAR(DataLoopNode::Node(22).Enthalpy, 41691.15, 0.0001);
EXPECT_NEAR(MSHeatPump(2).CompPartLoadRatio, 0.285417, 0.0001);

// Heating
QZnReq = 10000.00;
Expand Down