diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index aa2fbfcb986..8b9e5d173f4 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -128,6 +128,7 @@ void EIRPlantLoopHeatPump::simulate( state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex, "EIRPlantLoopHeatPump::simulate"); Real64 controlLoad = this->loadSideMassFlowRate * CurSpecHeat * (leavingSetpoint - loadSideInletTemp); + this->doPhysics(state, controlLoad); } else { this->doPhysics(state, CurLoad); @@ -136,13 +137,8 @@ void EIRPlantLoopHeatPump::simulate( this->resetReportingVariables(); } - // update nodes - PlantUtilities::SafeCopyPlantNode(state, this->loadSideNodes.inlet, this->loadSideNodes.outlet); - state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp; - if (this->waterSource) { - PlantUtilities::SafeCopyPlantNode(state, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet); - } - state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp; + // update report variables and nodes + this->report(state); } Real64 EIRPlantLoopHeatPump::getLoadSideOutletSetPointTemp(EnergyPlusData &state) const @@ -198,6 +194,7 @@ void EIRPlantLoopHeatPump::resetReportingVariables() this->fractionalDefrostTime = 0.0; this->partLoadRatio = 0.0; this->cyclingRatio = 0.0; + this->defrostPowerMultiplier = 1.0; } void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state, bool FirstHVACIteration) @@ -315,12 +312,6 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) { - - Real64 constexpr RH90 = 90.0; - Real64 constexpr RH60 = 60.0; - Real64 constexpr rangeRH = 30.0; - Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec; - // ideally the plant is going to ensure that we don't have a runflag=true when the load is invalid, but // I'm not sure we can count on that so we will do one check here to make sure we don't calculate things badly if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling && currentLoad >= 0.0) || @@ -329,6 +320,61 @@ void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) return; } + // dispatch to specific physics calculations based on the heat pump type + if (this->waterSource) { + this->doPhysicsWSHP(state, currentLoad); + } else if (this->airSource) { + this->doPhysicsASHP(state, currentLoad); + } +} + +void EIRPlantLoopHeatPump::doPhysicsWSHP(EnergyPlusData &state, Real64 currentLoad) +{ + + // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range + + Real64 availableCapacity = this->referenceCapacity; + Real64 partLoadRatio = 0.0; + + this->calcAvailableCapacity(state, currentLoad, availableCapacity, partLoadRatio); + this->setPartLoadAndCyclingRatio(state, partLoadRatio); + + // evaluate the actual current operating load side heat transfer rate + this->calcLoadSideHeatTransfer(state, availableCapacity); + + // no defrost calculation for WSHP + // calculate power usage from EIR curves + this->calcPowerUsage(state); + + // evaluate the source side heat transfer rate + this->calcSourceSideHeatTransferWSHP(state); +} + +void EIRPlantLoopHeatPump::doPhysicsASHP(EnergyPlusData &state, Real64 currentLoad) +{ + // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range + + Real64 availableCapacity = this->referenceCapacity; + Real64 partLoadRatio = 0.0; + + this->calcAvailableCapacity(state, currentLoad, availableCapacity, partLoadRatio); + this->setPartLoadAndCyclingRatio(state, partLoadRatio); + + // do defrost calculation if applicable + this->doDefrost(state, availableCapacity); + + // evaluate the actual current operating load side heat transfer rate + this->calcLoadSideHeatTransfer(state, availableCapacity); + + // calculate power usage from EIR curves + this->calcPowerUsage(state); + + // evaluate the source side heat transfer rate + this->calcSourceSideHeatTransferASHP(state); +} + +void EIRPlantLoopHeatPump::calcAvailableCapacity(EnergyPlusData &state, Real64 const currentLoad, Real64 &availableCapacity, Real64 &partLoadRatio) +{ // get setpoint on the load side outlet Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state); Real64 originalLoadSideOutletSPTemp = loadSideOutletSetpointTemp; @@ -336,8 +382,6 @@ void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range Real64 capacityModifierFuncTemp = 1.0; - Real64 availableCapacity = this->referenceCapacity; - Real64 partLoadRatio = 0.0; bool waterTempExceeded = false; // evaluate capacity modifier curve and determine load side heat transfer @@ -347,18 +391,8 @@ void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) availableCapacity = this->referenceCapacity * capacityModifierFuncTemp; - // apply heating mode dry outdoor (evaporator) coil correction factor for air-cooled equipment - if (this->capacityDryAirCurveIndex > 0 && this->airSource && state.dataEnvrn->OutRelHum < RH90) { // above 90% RH yields full capacity - Real64 dryCorrectionFactor = std::min(1.0, Curve::CurveValue(state, this->capacityDryAirCurveIndex, state.dataEnvrn->OutDryBulbTemp)); - if (state.dataEnvrn->OutRelHum <= RH60) { - // dry heating capacity correction factor is a function of outdoor dry-bulb temperature - availableCapacity *= dryCorrectionFactor; - } else { - // interpolation of heating capacity between wet and dry is based on outdoor relative humidity over 60%-90% range - Real64 semiDryFactor = dryCorrectionFactor + (1.0 - dryCorrectionFactor) * (1.0 - ((RH90 - state.dataEnvrn->OutRelHum) / rangeRH)); - availableCapacity *= semiDryFactor; - } - } + // apply air source HP dry air heating capacity correction + availableCapacity *= heatingCapacityModifierASHP(state); if (availableCapacity > 0) { partLoadRatio = std::clamp(std::abs(currentLoad) / availableCapacity, 0.0, 1.0); @@ -397,16 +431,131 @@ void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) } } + // check the curve values, reset to zero if negative + this->capModFTCurveCheck(state, loadSideOutletSetpointTemp, capacityModifierFuncTemp); +} + +Real64 EIRPlantLoopHeatPump::heatingCapacityModifierASHP(EnergyPlusData &state) const +{ + Real64 constexpr RH90 = 90.0; + Real64 constexpr RH60 = 60.0; + Real64 constexpr rangeRH = 30.0; + + // apply heating mode dry outdoor (evaporator) coil correction factor for air-cooled equipment + if (this->capacityDryAirCurveIndex > 0 && this->airSource && state.dataEnvrn->OutRelHum < RH90) { // above 90% RH yields full capacity + Real64 dryCorrectionFactor = std::min(1.0, Curve::CurveValue(state, this->capacityDryAirCurveIndex, state.dataEnvrn->OutDryBulbTemp)); + if (state.dataEnvrn->OutRelHum <= RH60) { + // dry heating capacity correction factor is a function of outdoor dry-bulb temperature + return dryCorrectionFactor; + } else { + // interpolation of heating capacity between wet and dry is based on outdoor relative humidity over 60%-90% range + Real64 semiDryFactor = dryCorrectionFactor + (1.0 - dryCorrectionFactor) * (1.0 - ((RH90 - state.dataEnvrn->OutRelHum) / rangeRH)); + return semiDryFactor; + } + } else { + // no correction needed, use full capacity + return 1.0; + } +} + +void EIRPlantLoopHeatPump::setPartLoadAndCyclingRatio(EnergyPlusData &state, Real64 &partLoadRatio) +{ + // Initialize cycling ratio to 1.0 Real64 cyclingRatio = 1.0; - Real64 operatingPLR = partLoadRatio; + + // Check if part load ratio is below the minimum threshold if (partLoadRatio < this->minimumPLR) { + // Adjust cycling ratio and set part load ratio to minimum cyclingRatio = partLoadRatio / this->minimumPLR; partLoadRatio = this->minimumPLR; - operatingPLR = partLoadRatio * cyclingRatio; } + + // update class member variables this->partLoadRatio = partLoadRatio; this->cyclingRatio = cyclingRatio; +} + +void EIRPlantLoopHeatPump::calcLoadSideHeatTransfer(EnergyPlusData &state, Real64 const availableCapacity) +{ + // evaluate the actual current operating load side heat transfer rate + auto &thisLoadPlantLoop = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum); + Real64 CpLoad = FluidProperties::GetSpecificHeatGlycol(state, + thisLoadPlantLoop.FluidName, + state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp, + thisLoadPlantLoop.FluidIndex, + "EIRPlantLoopHeatPump::calcLoadSideHeatTransfer()"); + + Real64 const operatingPLR = this->partLoadRatio * this->cyclingRatio; + this->loadSideHeatTransfer = availableCapacity * operatingPLR; + + // calculate load side outlet conditions + Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad; + this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp); + + // now what to do here if outlet water temp exceeds limit based on HW supply temp limit curves? + // currentLoad will be met and there should? be some adjustment based on outlet water temp limit? +} + +void EIRPlantLoopHeatPump::calcPowerUsage(EnergyPlusData &state) +{ + + // calculate power usage from EIR curves + Real64 eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp); + Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, this->partLoadRatio); + + // check curves value and resets to zero if negative + this->eirModCurveCheck(state, eirModifierFuncTemp, eirModifierFuncPLR); + + // compute power usage + this->powerUsage = (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp * this->defrostPowerMultiplier * + this->cyclingRatio; +} + +void EIRPlantLoopHeatPump::calcSourceSideHeatTransferWSHP(EnergyPlusData &state) +{ + + // energy balance on heat pump + this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage); + + // calculate source side outlet conditions + auto &thisSourcePlantLoop = state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum); + Real64 const CpSrc = FluidProperties::GetSpecificHeatGlycol(state, + thisSourcePlantLoop.FluidName, + this->sourceSideInletTemp, + thisSourcePlantLoop.FluidIndex, + "EIRPlantLoopHeatPump::calcSourceSideHeatTransferWSHP()"); + Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc; + this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp); + + if (this->waterSource && abs(this->sourceSideOutletTemp - this->sourceSideInletTemp) > 100.0) { // whoaa out of range happenings on water loop + // + // TODO setup recurring error warning? + // lets do something different than fatal the simulation + if ((this->sourceSideMassFlowRate / this->sourceSideDesignMassFlowRate) < 0.01) { // current source side flow is 1% of design max + // just send it all to skin losses and leave the fluid temperature alone + this->sourceSideOutletTemp = this->sourceSideInletTemp; + } else if (this->sourceSideOutletTemp > this->sourceSideInletTemp) { + this->sourceSideOutletTemp = this->sourceSideInletTemp + 100.0; // cap it at 100C delta + + } else if (this->sourceSideOutletTemp < this->sourceSideInletTemp) { + this->sourceSideOutletTemp = this->sourceSideInletTemp - 100.0; // cap it at 100C delta + } + } +} +void EIRPlantLoopHeatPump::calcSourceSideHeatTransferASHP(EnergyPlusData &state) +{ + // energy balance on heat pump + this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage); + + // calculate source side outlet conditions + Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat); + Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc; + this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp); +} + +void EIRPlantLoopHeatPump::capModFTCurveCheck(EnergyPlusData &state, const Real64 loadSideOutletSetpointTemp, Real64 &capacityModifierFuncTemp) +{ if (capacityModifierFuncTemp < 0.0) { if (this->capModFTErrorIndex == 0) { ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], this->name)); @@ -427,10 +576,53 @@ void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) capacityModifierFuncTemp); capacityModifierFuncTemp = 0.0; } +} + +void EIRPlantLoopHeatPump::eirModCurveCheck(EnergyPlusData &state, Real64 &eirModifierFuncTemp, Real64 &eirModifierFuncPLR) +{ + if (eirModifierFuncTemp < 0.0) { + if (this->eirModFTErrorIndex == 0) { + ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], this->name)); + ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp)); + ShowContinueError(state, + format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.", + this->loadSideOutletTemp, + this->sourceSideInletTemp)); + ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation."); + } + ShowRecurringWarningErrorAtEnd(state, + format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...", + DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], + this->name), + this->eirModFTErrorIndex, + eirModifierFuncTemp, + eirModifierFuncTemp); + eirModifierFuncTemp = 0.0; + } + + if (eirModifierFuncPLR < 0.0) { + if (this->eirModFPLRErrorIndex == 0) { + ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], this->name)); + ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR)); + ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", this->partLoadRatio)); + ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation."); + } + ShowRecurringWarningErrorAtEnd(state, + format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...", + DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], + this->name), + this->eirModFPLRErrorIndex, + eirModifierFuncPLR, + eirModifierFuncPLR); + eirModifierFuncPLR = 0.0; + } +} +void EIRPlantLoopHeatPump::doDefrost(EnergyPlusData &state, Real64 &availableCapacity) +{ // Initializing defrost adjustment factors - Real64 HeatingCapacityMultiplier = 1.0; Real64 InputPowerMultiplier = 1.0; + Real64 HeatingCapacityMultiplier = 1.0; // Check outdoor temperature to determine of defrost is active if (this->defrostAvailable && state.dataEnvrn->OutDryBulbTemp <= this->maxOutdoorTemperatureDefrost) { @@ -473,6 +665,7 @@ void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) // cycles of defrost per hour Real64 thisHourDefrostCycles = Curve::CurveValue(state, this->defrostFreqCurveIndex, state.dataEnvrn->OutDryBulbTemp); // is directly proportional to the ratio of capacity used for that hour (PLR) + Real64 const operatingPLR = this->partLoadRatio * this->cyclingRatio; thisHourDefrostCycles *= operatingPLR; // fraction of heat load per cycle of defrost Real64 thisHourDefrostHeatLoad = 0.0; @@ -506,102 +699,8 @@ void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) this->fractionalDefrostTime = 0.0; } availableCapacity *= HeatingCapacityMultiplier; - this->defrostEnergy = this->defrostEnergyRate * reportingInterval; - - // evaluate the actual current operating load side heat transfer rate - auto &thisLoadPlantLoop = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum); - Real64 CpLoad = FluidProperties::GetSpecificHeatGlycol(state, - thisLoadPlantLoop.FluidName, - state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp, - thisLoadPlantLoop.FluidIndex, - "EIRPlantLoopHeatPump::doPhysics()"); - this->loadSideHeatTransfer = availableCapacity * operatingPLR; - this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval; - - // calculate load side outlet conditions - Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad; - this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp); - - // now what to do here if outlet water temp exceeds limit based on HW supply temp limit curves? - // currentLoad will be met and there should? be some adjustment based on outlet water temp limit? - - // calculate power usage from EIR curves - Real64 eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp); - - if (eirModifierFuncTemp < 0.0) { - if (this->eirModFTErrorIndex == 0) { - ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], this->name)); - ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp)); - ShowContinueError(state, - format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.", - this->loadSideOutletTemp, - this->sourceSideInletTemp)); - ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation."); - } - ShowRecurringWarningErrorAtEnd(state, - format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...", - DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], - this->name), - this->eirModFTErrorIndex, - eirModifierFuncTemp, - eirModifierFuncTemp); - eirModifierFuncTemp = 0.0; - } - - Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, this->partLoadRatio); - - if (eirModifierFuncPLR < 0.0) { - if (this->eirModFPLRErrorIndex == 0) { - ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], this->name)); - ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR)); - ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", this->partLoadRatio)); - ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation."); - } - ShowRecurringWarningErrorAtEnd(state, - format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...", - DataPlant::PlantEquipTypeNames[static_cast(this->EIRHPType)], - this->name), - this->eirModFPLRErrorIndex, - eirModifierFuncPLR, - eirModifierFuncPLR); - eirModifierFuncPLR = 0.0; - } - - this->powerUsage = - (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp * InputPowerMultiplier * this->cyclingRatio; - this->powerEnergy = this->powerUsage * reportingInterval; - - // energy balance on heat pump - this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage); - this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval; - - // calculate source side outlet conditions - Real64 CpSrc; - if (this->waterSource) { - auto &thisSourcePlantLoop = state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum); - CpSrc = FluidProperties::GetSpecificHeatGlycol( - state, thisSourcePlantLoop.FluidName, this->sourceSideInletTemp, thisSourcePlantLoop.FluidIndex, "EIRPlantLoopHeatPump::doPhysics()"); - } else { - CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat); - } - // this->sourceSideCp = CpSrc; // debuging variable - Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc; - this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp); - - if (this->waterSource && abs(this->sourceSideOutletTemp - this->sourceSideInletTemp) > 100.0) { // whoaa out of range happenings on water loop - // - // TODO setup recurring error warning? - // lets do something different than fatal the simulation - if ((this->sourceSideMassFlowRate / this->sourceSideDesignMassFlowRate) < 0.01) { // current source side flow is 1% of design max - // just send it all to skin losses and leave the fluid temperature alone - this->sourceSideOutletTemp = this->sourceSideInletTemp; - } else if (this->sourceSideOutletTemp > this->sourceSideInletTemp) { - this->sourceSideOutletTemp = this->sourceSideInletTemp + 100.0; // cap it at 100C delta - - } else if (this->sourceSideOutletTemp < this->sourceSideInletTemp) { - this->sourceSideOutletTemp = this->sourceSideInletTemp - 100.0; // cap it at 100C delta - } - } + // update class member variables + this->defrostPowerMultiplier = InputPowerMultiplier; } void EIRPlantLoopHeatPump::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation) @@ -1832,6 +1931,25 @@ void EIRPlantLoopHeatPump::oneTimeInit(EnergyPlusData &state) } } +void EIRPlantLoopHeatPump::report(EnergyPlusData &state) +{ + + Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec; + + this->defrostEnergy = this->defrostEnergyRate * reportingInterval; + this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval; + this->powerEnergy = this->powerUsage * reportingInterval; + this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval; + + // update nodes + PlantUtilities::SafeCopyPlantNode(state, this->loadSideNodes.inlet, this->loadSideNodes.outlet); + state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp; + if (this->waterSource) { + PlantUtilities::SafeCopyPlantNode(state, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet); + } + state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp; +} + // From here on, the Fuel Fired Heat Pump module EIRFuelFiredHeatPump // Enum string definitions static constexpr std::array(EIRFuelFiredHeatPump::OATempCurveVar::Num)> OATempCurveVarNamesUC = {"DRYBULB", @@ -1862,7 +1980,7 @@ void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) auto &thisInletNode = state.dataLoopNodes->Node(this->loadSideNodes.inlet); auto &thisOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet); auto &sim_component = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc); - if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating && currentLoad <= 0.0) { + if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating && currentLoad <= 0.0)) { if (sim_component.FlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) this->loadSideMassFlowRate = thisInletNode.MassFlowRate; this->resetReportingVariables(); return; @@ -2082,7 +2200,6 @@ void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) // this->loadSideHeatTransfer = availableCapacity * partLoadRatio; this->loadSideHeatTransfer = availableCapacity * partLoadRatio; // (partLoadRatio >= this->minPLR ? partLoadRatio : 0.0); - this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval; // calculate load side outlet conditions Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad; @@ -2248,13 +2365,9 @@ void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) } this->powerUsage += this->standbyElecPower; - this->fuelEnergy = this->fuelRate * reportingInterval; - this->powerEnergy = this->powerUsage * reportingInterval; - // energy balance on heat pump // this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage); this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->fuelRate + this->powerUsage - this->standbyElecPower); - this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval; // calculate source side outlet conditions Real64 CpSrc = 0.0; @@ -3057,4 +3170,19 @@ void EIRFuelFiredHeatPump::oneTimeInit(EnergyPlusData &state) } } +void EIRFuelFiredHeatPump::report(EnergyPlusData &state) +{ + Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec; + + this->fuelEnergy = this->fuelRate * reportingInterval; + this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval; + this->powerEnergy = this->powerUsage * reportingInterval; + this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval; + + // update nodes + PlantUtilities::SafeCopyPlantNode(state, this->loadSideNodes.inlet, this->loadSideNodes.outlet); + state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp; + state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp; +} + } // namespace EnergyPlus::EIRPlantLoopHeatPumps diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.hh b/src/EnergyPlus/PlantLoopHeatPumpEIR.hh index 970987c95bf..4e252c295a0 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.hh +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.hh @@ -206,6 +206,7 @@ namespace EIRPlantLoopHeatPumps { Real64 defrostEnergy = 0.0; Real64 fractionalDefrostTime = 0.0; Real64 maxOutdoorTemperatureDefrost = 0.0; + Real64 defrostPowerMultiplier = 1.0; // defrost power adjustment factor // a couple worker functions to easily allow merging of cooling and heating operations std::function calcLoadOutletTemp; @@ -229,12 +230,38 @@ namespace EIRPlantLoopHeatPumps { virtual void doPhysics(EnergyPlusData &state, Real64 currentLoad); + void doPhysicsWSHP(EnergyPlusData &state, Real64 currentLoad); + + void doPhysicsASHP(EnergyPlusData &state, Real64 currentLoad); + + void calcAvailableCapacity(EnergyPlusData &state, Real64 const currentLoad, Real64 &availableCapacity, Real64 &partLoadRatio); + + Real64 heatingCapacityModifierASHP(EnergyPlusData &state) const; + + void setPartLoadAndCyclingRatio(EnergyPlusData &state, Real64 &partLoadRatio); + + void calcLoadSideHeatTransfer(EnergyPlusData &state, Real64 const availableCapacity); + + void calcPowerUsage(EnergyPlusData &state); + + void calcSourceSideHeatTransferWSHP(EnergyPlusData &state); + + void calcSourceSideHeatTransferASHP(EnergyPlusData &state); + + virtual void report(EnergyPlusData &state); + void sizeLoadSide(EnergyPlusData &state); void sizeSrcSideWSHP(EnergyPlusData &state); void sizeSrcSideASHP(EnergyPlusData &state); + void doDefrost(EnergyPlusData &state, Real64 &AvailableCapacity); + + void capModFTCurveCheck(EnergyPlusData &state, const Real64 loadSideOutletSPTemp, Real64 &capModFTemp); + + void eirModCurveCheck(EnergyPlusData &state, Real64 &eirModFTemp, Real64 &eirModFPLR); + Real64 getLoadSideOutletSetPointTemp(EnergyPlusData &state) const; void setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration); @@ -350,6 +377,7 @@ namespace EIRPlantLoopHeatPumps { static void pairUpCompanionCoils(EnergyPlusData &state); static void processInputForEIRPLHP(EnergyPlusData &state); void oneTimeInit(EnergyPlusData &state); + void report(EnergyPlusData &state); // New or specialized functions for derived struct virtual ~EIRFuelFiredHeatPump() = default; diff --git a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc index 682592cab74..a1cdaf628d2 100644 --- a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc +++ b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc @@ -3499,6 +3499,7 @@ TEST_F(EnergyPlusFixture, Test_Curve_Negative_Energy) thisCoolingPLHP->loadSideInletTemp = 20; thisCoolingPLHP->sourceSideInletTemp = 20; thisCoolingPLHP->doPhysics(*state, curLoad); + thisCoolingPLHP->report(*state); // Power and energy are now zero since the curve is reset with zero values EXPECT_NEAR(thisCoolingPLHP->powerUsage, 0.000, 1e-3);