Skip to content

Conversation

@lymereJ
Copy link
Collaborator

@lymereJ lymereJ commented Apr 28, 2023

Pull request overview

  • Address 2 speed operation with Air Economizer in UnitarySystem  #6109
  • This pull request adds a new feature to EnergyPlus that enables users to simulate an "economizer first" approach when modeling an economizer in a sensible-only load-controlled unitary systems with multispeed (or variable speed) fans and multispeed (or variable speed) coils. The new approach lets the economizer operation dictates the system flow rate instead of being decided based on the cooling coil speed (current approach). This new feature allows to simulate the performance of commercially available economizer controller for such systems more closely.

Pull Request Author

Add to this list or remove from it as applicable. This is a simple templated set of guidelines.

  • Title of PR should be user-synopsis style (clearly understandable in a standalone changelog context)
  • Label the PR with at least one of: Defect, Refactoring, NewFeature, Performance, and/or DoNoPublish
  • Pull requests that impact EnergyPlus code must also include unit tests to cover enhancement or defect repair
  • Author should provide a "walkthrough" of relevant code changes using a GitHub code review comment process
  • If any diffs are expected, author must demonstrate they are justified using plots and descriptions

Reviewer

This will not be exhaustively relevant to every PR.

  • Perform a Code Review on GitHub
  • If branch is behind develop, merge develop and build locally to check for side effects of the merge
  • If defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
  • If feature, test running new feature, try creative ways to break it
  • CI status: all green or justified
  • Check that performance is not impacted (CI Linux results include performance check)
  • Run Unit Test(s) locally
  • Check any new function arguments for performance impacts
  • Verify IDF naming conventions and styles, memos and notes and defaults
  • If new idf included, locally check the err file and other outputs

@lymereJ lymereJ added NewFeature Includes code to add a new feature to EnergyPlus IDDChange Code changes impact the IDD file (cannot be merged after IO freeze) labels Apr 28, 2023
@lymereJ lymereJ added this to the EnergyPlus 23.2 IOFreeze milestone Apr 28, 2023
@nrel-bot-2b
Copy link

@lymereJ @Myoldmopar it has been 28 days since this pull request was last updated.

1 similar comment
@nrel-bot
Copy link

@lymereJ @Myoldmopar it has been 28 days since this pull request was last updated.

…ld overcool and 100% at the lower speed would not be enough. The approach used is to run at both speeds to meet the load and average the flow rate. Add a new function to get the a fan DT at a specific mass flow rate and airflow ratio.
int unitarySystemNum = UtilityRoutines::FindItemInList(
unitarySystemName, state.dataUnitarySystems->unitarySys, state.dataUnitarySystems->numUnitarySystems);
if (state.dataUnitarySystems->unitarySys[unitarySystemNum - 1].m_ControlType ==
UnitarySystems::UnitarySys::UnitarySysCtrlType::Load) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not part of this PR, but I would like to point out that in this fully-resolved name, UnitarySys appears THREE times! So, what you're telling me is that this is related to unitary systems?

// IF (UnitarySystem(UnitarySysNum)%AirFlowControl .EQ. UseCompressorOnFlow) THEN
if (this->m_FanOpMode == DataHVACGlobals::ContFanCycCoil) {
if (CoolSpeedNum == 0) {
state.dataUnitarySystems->CompOffMassFlow = this->MaxNoCoolHeatAirMassFlow;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Random questions, why are these essentially global variables?

@lymereJ
Copy link
Collaborator Author

lymereJ commented Aug 18, 2023 via email

@lymereJ
Copy link
Collaborator Author

lymereJ commented Aug 18, 2023

Well, I broke everything, but I think that I know what I did wrong, I'll reset to the last good commit, re-address the conflicts, and test locally first before pushing.

Ok, I reverted the previous merge and re-did it correctly, I think. I also updated the new example file following the latest changes to develop, and made changes based on Richard's comments. The example file ran fine locally, I'm hoping that the CI will come back "clean" (as in the same types/magnitude of diffs as before).

@rraustad, here are the plots you requested for three consecutive days. Outdoor air is in orange for the InterlockedWithMechanicalCooling and maroon for the EconomizerFirst. Blue is the supply fan air flow rate for the EconomizerFirst case. The bottom panel shows the cooling rate.

  • System 1

image

  • System 2

image

  • System 3

image

  • System 4

image

@rraustad
Copy link
Contributor

rraustad commented Aug 18, 2023

@lymereJ what I was looking for was an increase in fan flow without compressor operation. It appears to me that all these plots show that econoFirst does that. @Myoldmopar from what I see this branch is doing exactly what is intended.

The only pause I have is this line in MixedAir. The mixer return node is set by the sum of the zone returns. I don't think that should be calculated/changed in the mixer code. It may be that this is the correct flow rate for this calculation of the mixer node flows, but I think the correct flow rate will wind up at the mixer return node naturally. It's the concept of stomping on what other components have already set.

state.dataLoopNodes->Node(thisOAController.RetNode).MassFlowRate = thisOAController.MixMassFlow - thisOAController.ExhMassFlow;

@lymereJ if this line is deleted, what happens?

@Myoldmopar
Copy link
Member

Thanks @rraustad, that's great to hear! I will kick off a local build of this branch and if we don't hear back from @lymereJ I can even setup a second build with that line removed and run tests/regressions.

Other than that, CI results look much better, only very minor small diffs and warmup convergence style changes. So I agree this is probably very close.

@rraustad
Copy link
Contributor

rraustad commented Aug 18, 2023

The mixer return node is the air loop supply side return, which is "connected" to the demand side outlet node. That node, the mixer return, is set/updated by the air loop manager and is a node that is tested for convergence. This is from the new example file UnitarySystem_MultiSpeedDX_EconoStaging.

OutdoorAir:Mixer,
  Sys 1 Furnace DX Cool OA Mixing Box,  !- Name
  Sys 1 Furnace DX Cool Mixed Air Outlet,  !- Mixed Air Node Name
  Sys 1 Furnace DX Cool Outdoor Air Inlet,  !- Outdoor Air Stream Node Name
  Sys 1 Furnace DX Cool Relief Air Outlet,  !- Relief Air Stream Node Name
  Sys 1 Furnace DX Cool Air Loop Inlet;  !- Return Air Stream Node Name   <-- this node

AirLoopHVAC,
  Sys 1 Furnace DX Cool,   !- Name
  ,                        !- Controller List Name
  Sys 1 Furnace DX Cool Availability Managers,  !- Availability Manager List Name
  autosize,                !- Design Supply Air Flow Rate {m3/s}
  Sys 1 Furnace DX Cool Branches,  !- Branch List Name
  ,                        !- Connector List Name
  Sys 1 Furnace DX Cool Air Loop Inlet,  !- Supply Side Inlet Node Name  <-- is this node
  Sys 1 Furnace DX Cool Return Air Outlet,  !- Demand Side Outlet Node Name
  Sys 1 Furnace DX Cool Supply Path Inlet,  !- Demand Side Inlet Node Names
  Sys 1 Furnace DX Cool Heating Coil Outlet;  !- Supply Side Outlet Node Names

That node gets updated/set in:

HVACInterfaceManager::UpdateHVACInterface(state,
                                          ZoneGroupNum,
                                          DataConvergParams::CalledFrom::AirSystemDemandSide,
                                          state.dataAirLoop->AirToZoneNodeInfo(ZoneGroupNum).ZoneEquipReturnNodeNum(RetAirPathNum),
                                          state.dataAirLoop->AirToZoneNodeInfo(ZoneGroupNum).AirLoopReturnNodeNum(RetAirPathNum),
                                          SimAir);

@Myoldmopar
Copy link
Member

I just built this branch and ran it happily, all tests pass. I did a second build of this branch where I commented that line out, once again it built and ran all tests passing. I then ran a regression between the two branches and got zero diffs whatsoever. I think you are right that that line is not necessary, so we can just comment/remove that line and maybe this will be ready to merge. If that's correct, I'll push that up shortly, or @lymereJ feel free to chime in.

@rraustad
Copy link
Contributor

I'm surprised there were no diffs, but then that's a very good thing. Delete that line and then this branch can merge.

@Myoldmopar
Copy link
Member

Pushed 👍

@Myoldmopar
Copy link
Member

I'll let the basic GHA runs get going then merge shortly.

@Myoldmopar
Copy link
Member

I was hoping to catch it before Decent got started, but missed it. I'm going to merge this but I"ll leave the branch temporarily so that Decent can finish its runs. Thanks @lymereJ for this enhancement. Thanks @rraustad for so much review; it seems like this PR is triggering some interesting refactoring ideas to me.

@Myoldmopar Myoldmopar merged commit 273aede into develop Aug 18, 2023
@lymereJ
Copy link
Collaborator Author

lymereJ commented Aug 18, 2023

Sorry for only getting back now. I wish I would have better documented the reason behind adding that line, right now, I got nothing. Perhaps it's a leftover from me testing things. I was going to suggest to comment it out to leave a trace in case an issue comes up but it looks like that's exactly what you did, thanks! For the record, I did run a full annual simulation for the new example file, the table files are the same, and I see some very negligible diffs in the ESO.

@lymereJ lymereJ linked an issue Aug 18, 2023 that may be closed by this pull request
auto outdoorAirController = state.dataMixedAir->OAController(this->OAControllerIndex);
using Psychrometrics::PsyCpAirFnW;
Real64 cpAir = PsyCpAirFnW(state.dataLoopNodes->Node(outdoorAirController.InletNode).HumRat);
Real64 zoneTemp = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(this->ControlZoneNum).ZoneNode).Temp;
Copy link
Contributor

Choose a reason for hiding this comment

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

@lymereJ if the UnitarySystem is set up for Setpoint control, this->ControlZoneNum will be 0. Can you quickly test that to make sure there is no crash here, or check for Load control early on so this doesn't crash?

Copy link
Collaborator Author

@lymereJ lymereJ Aug 18, 2023

Choose a reason for hiding this comment

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

@rraustad - We do check for load control early on but we just use the outcome to display a warning the system is controlled by setpoint, so I think it will crash. I can add a check for type of control in the if statement below so we don't perform economizer staging calculations if the system is setpoint controlled.
From:

this->m_EconoSpeedNum = 0;
if (this->OAControllerEconomizerStagingType == DataHVACGlobals::EconomizerStagingType::EconomizerFirst) {
    manageEconomizerStagingOperation(state, AirLoopNum, FirstHVACIteration, ZoneLoad);
}

To:

this->m_EconoSpeedNum = 0;
if (this->OAControllerEconomizerStagingType == DataHVACGlobals::EconomizerStagingType::EconomizerFirst &&
    this->m_ControlType == UnitarySysCtrlType::Load) {
    manageEconomizerStagingOperation(state, AirLoopNum, FirstHVACIteration, ZoneLoad);
}

Would that work for you?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I checked and I don't think we need to do anything here. manageEconomizerStagingOperation is only called from controlUnitarySystemOutput which is only used for load based systems. So if a model uses the new Controller:OutdoorAir input, it won't change anything to the simulation. Moreover, the PR added a warning message to let the user know that that if that input is selected in that context it won't do anything.

state, firstHVACIteration, mixedAirFlowRate, mixedAirFlowRate / this->m_CoolMassFlowRate[this->m_NumOfSpeedCooling]);
// determine new mixed air setpoint
Real64 newMixedAirSP = zoneTemp - std::abs(zoneLoad) / (cpAir * mixedAirFlowRate);
state.dataLoopNodes->Node(mixedAirNode).TempSetPoint = newMixedAirSP - fanDTAtMixedAirFlowRate;
Copy link
Contributor

Choose a reason for hiding this comment

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

@lymereJ this new control puts a SP on the mixed air node that tries to control the OA system to meet the zone load. That's fine, but 1) is stomping on the mixed air SPM, and 2) the user does not know anything about this override. In fact I'm not even sure this is doing anything because the SPMs and OA system are simulated before the UnitarySystem. Here's another case where I want to know what happens if this SP control is removed. If it's necessary then fine, but the user needs to know what is happening in their simulation. If this in fact is needed then I think this should be described in the docs and the "Unitary System Mixed Air Setpoint Temperature" be reported out to the user.


// recalculate the outdoor air fraction to meet the new mixed air setpoint at the new mixed air flow rate
MixedAir::ManageOutsideAirSystem(
state, state.dataAirLoop->OutsideAirSys(this->OASysIndex).Name, firstHVACIteration, airLoopNum, this->OASysIndex);
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see, the OA system gets called from the UnitarySystem. I previously thought that only the air loop control properties were set in UnitarySystem. So that means the mixed air set point IS getting used, and that calculation of that SP is necessary. This is pointing to a new report for "Unitary System Mixed Air Setpoint Temperature".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was just going through your comments one more time, so just to confirm, there's is nothing to add here, is that correct? The user should be able to get the setpoint set by the unitary system by requesting the system node setpoint temperature output variable.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think viewing the SP on the node will be sufficient for now.

@Myoldmopar
Copy link
Member

It does look like there is one fatal error, but only showing up on Windows oddly: https://myoldmopar.github.io/EnergyPlusBuildResults/EnergyPlus-273aede840425898126ac7f34ce658436f17297f-Win64-Windows-10-VisualStudio-16.html#test

Please try to reproduce that and fix it when you push any additional commits.

@lymereJ
Copy link
Collaborator Author

lymereJ commented Aug 18, 2023

@Myoldmopar - Really odd, I ran the test on my Windows machine and it passes.

Copy link
Contributor

@rraustad rraustad left a comment

Choose a reason for hiding this comment

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

Lot's of work here to include OA system operation with UnitarySystem performance. Nice effort.


// recalculate the outdoor air fraction to meet the new mixed air setpoint at the new mixed air flow rate
MixedAir::ManageOutsideAirSystem(
state, state.dataAirLoop->OutsideAirSys(this->OASysIndex).Name, firstHVACIteration, airLoopNum, this->OASysIndex);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think viewing the SP on the node will be sufficient for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

IDDChange Code changes impact the IDD file (cannot be merged after IO freeze) NewFeature Includes code to add a new feature to EnergyPlus

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 speed operation with Air Economizer in UnitarySystem

10 participants