Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
06d2da9
Run final surface heat balance when there are any internal heat sourc…
joseph-robertson Aug 6, 2026
a406a28
Clean up photovoltaics.
joseph-robertson Aug 6, 2026
a063151
Only run final surface heat balance after initialization to avoid ste…
joseph-robertson Aug 6, 2026
54a04f3
Formatting.
joseph-robertson Aug 6, 2026
49bc055
Try another approach, move SurfQsrcHist updates after ManageAirHeatBa…
joseph-robertson Aug 7, 2026
f1dd714
Try yet another approach, call UpdateFinalSurfaceHeatBalance only whe…
joseph-robertson Aug 7, 2026
aa2000b
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Aug 10, 2026
19f22c1
Only calc outside surf heat balance again for integrated pv modes.
joseph-robertson Aug 10, 2026
ec2f6b5
Formatting and variable name changes.
joseph-robertson Aug 11, 2026
09cb46c
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Aug 11, 2026
6da13ea
Add a unit test for QPVSysSource and its affect on SurfQsrcHist and S…
joseph-robertson Aug 11, 2026
cf62072
Formatting.
joseph-robertson Aug 11, 2026
68b7e01
Refactor new HasBuildingIntegratedPV method so modern integrity passes.
joseph-robertson Aug 11, 2026
3acdf85
Try a different approach involving refactoring PV methods so we can e…
joseph-robertson Aug 13, 2026
7ed8f0a
Clean up Generator:Photovoltaic docs.
joseph-robertson Aug 13, 2026
ee65e23
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Aug 13, 2026
a9f9458
Formatting, and guard when no PV generators.
joseph-robertson Aug 13, 2026
c93ba68
Updates for other integration modes, comments, tests, etc.
joseph-robertson Aug 13, 2026
ae59c3a
Test rename.
joseph-robertson Aug 13, 2026
c58476f
Formatting.
joseph-robertson Aug 14, 2026
223ccca
Formatting.
joseph-robertson Aug 14, 2026
43a805d
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Aug 14, 2026
7ae8c37
Update HVACManager for new resim flag; add comments to new unit tests.
joseph-robertson Aug 14, 2026
c983629
Formatting.
joseph-robertson Aug 17, 2026
f44a337
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Aug 17, 2026
980c1b0
Update new HeatBalanceSurfaceManager unit test to avoid segfault.
joseph-robertson Aug 17, 2026
fbf25b0
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Aug 24, 2026
6c7125c
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Sep 3, 2026
523c557
Fix zone load prediction after PV surface resimulation.
joseph-robertson Sep 3, 2026
228b2b1
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Sep 9, 2026
4eda9f1
Merge branch 'develop' into pv-int-heat-source
joseph-robertson Sep 10, 2026
5ce484e
Call SimSurfaceCoupledPV a final time if surface heat balance functio…
joseph-robertson Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4473,7 +4473,7 @@ \subsubsection{Inputs}\label{inputs-20-000}

\subsubsection{Outputs}\label{outputs-11-001}

Using the Generator:PV:Simple object makes a number of output variables available.\textbackslash{}
Using the Generator:Photovoltaic object makes a number of output variables available:

\begin{itemize}
\item
Expand All @@ -4492,19 +4492,19 @@ \subsubsection{Outputs}\label{outputs-11-001}

\paragraph{Generator Produced DC Electricity Rate {[}W{]}}\label{generator-produced-dc-electric-power-w-2}

This output variable is the power of DC electricity produced by the PV array in Watts..
This output variable is the power of DC electricity produced by the PV array in Watts.

\paragraph{Generator Produced DC Electricity Energy {[}J{]}}\label{generator-produced-dc-electric-energy-j}

This output variable is the energy in DC electricity produced by the PV array, in Joules.

\paragraph{Generator PV Array Efficiency {[]}}\label{generator-pv-array-efficiency}

This output variable is the resulting efficiency of the PV array .
This output variable is the resulting efficiency of the PV array.

The two more detailed PV models also have the following output variables.
The two more detailed PV models (PhotovoltaicPerformance:EquivalentOne-Diode and PhotovoltaicPerformance:Sandia) also have the following output variables:

\paragraph{Generator PV Cell Temperature {[} \SI{}{\celsius}{]}}\label{generator-pv-cell-temperature-c}
\paragraph{Generator PV Cell Temperature {[}\SI{}{\celsius}{]}}\label{generator-pv-cell-temperature-c}

This output variable represents the temperature of the solar cell used in the calculation of cell performance.

Expand Down
5 changes: 3 additions & 2 deletions src/EnergyPlus/DataHVACGlobals.hh
Original file line number Diff line number Diff line change
Expand Up @@ -457,8 +457,9 @@ struct HVACGlobalsData : BaseGlobalStruct
Real64 deviationFromSetPtThresholdHtg = -0.2; // heating threshold for reporting setpoint deviation
Real64 deviationFromSetPtThresholdClg = 0.2; // cooling threshold for reporting setpoint deviation

bool SimAirLoopsFlag = false; // True when the air loops need to be (re)simulated
bool SimElecCircuitsFlag = false; // True when electic circuits need to be (re)simulated
bool SimAirLoopsFlag = false; // True when the air loops need to be (re)simulated
bool SimElecCircuitsFlag = false; // True when electic circuits need to be (re)simulated
bool PVSurfaceHeatBalanceResimFlag = false;
bool SimPlantLoopsFlag = false; // True when the main plant loops need to be (re)simulated
bool SimZoneEquipmentFlag = false; // True when zone equipment components need to be (re)simulated
bool SimNonZoneEquipmentFlag = false; // True when non-zone equipment components need to be (re)simulated
Expand Down
5 changes: 4 additions & 1 deletion src/EnergyPlus/DataPhotovoltaics.hh
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,10 @@ namespace DataPhotovoltaics {
int ExtVentCavPtr; // pointer to Exterior Vented Cavity EXTERIOR VENTED CAVITY
int PVTPtr; // pointer to PVT model
Real64 SurfaceSink; // PV power "sink" for integration
PVReportVariables Report; // report variables
bool SurfaceCouplingRunFlag = true; // run flag used before the surface heat balance
Real64 SurfaceCouplingSource = 0.0; // last integrated PV sink applied to its coupled model
bool SurfaceCouplingNeedsResim = false;
PVReportVariables Report; // report variables
// nested structs for user input parameters
SimplePVParamsStruct SimplePVModule; // simple model input params
TRNSYSPVModuleParamsStruct TRNSYSPVModule; // equivalent one-diode input params
Expand Down
13 changes: 12 additions & 1 deletion src/EnergyPlus/HVACManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#include <EnergyPlus/General.hh>
#include <EnergyPlus/HVACManager.hh>
#include <EnergyPlus/HVACSizingSimulationManager.hh>
#include <EnergyPlus/HeatBalanceSurfaceManager.hh>
#include <EnergyPlus/IceThermalStorage.hh>
#include <EnergyPlus/IndoorGreen.hh>
#include <EnergyPlus/InternalHeatGains.hh>
Expand Down Expand Up @@ -881,7 +882,8 @@ void SimHVAC(EnergyPlusData &state)
// Main iteration loop for HVAC. If any of the simulation flags are
// true, then specific components must be resimulated.
while ((state.dataHVACGlobal->SimAirLoopsFlag || state.dataHVACGlobal->SimZoneEquipmentFlag || state.dataHVACGlobal->SimNonZoneEquipmentFlag ||
state.dataHVACGlobal->SimPlantLoopsFlag || state.dataHVACGlobal->SimElecCircuitsFlag) &&
state.dataHVACGlobal->SimPlantLoopsFlag || state.dataHVACGlobal->SimElecCircuitsFlag ||
state.dataHVACGlobal->PVSurfaceHeatBalanceResimFlag) &&
(state.dataHVACMgr->HVACManageIteration <= state.dataConvergeParams->MaxIter)) {

if (state.dataGlobal->stopSimulation) {
Expand Down Expand Up @@ -1805,6 +1807,15 @@ void SimSelectedEquipment(EnergyPlusData &state,
}
PlantUtilities::ResetAllPlantInterConnectFlags(state);

if (state.dataHVACGlobal->PVSurfaceHeatBalanceResimFlag) {
HeatBalanceSurfaceManager::ResimulateSurfaceHeatBalanceForPV(state);
ZoneTempPredictorCorrector::PredictSystemLoads(
state, state.dataHVACGlobal->ShortenTimeStepSys, state.dataHVACGlobal->UseZoneTimeStepHistory, state.dataGlobal->TimeStepZone);
// The updated demand must be consumed during this HVAC iteration, even when the PV surface request
// was the only flag keeping the iteration loop active.
SimZoneEquipment = true;
}

if (state.dataGlobal->BeginEnvrnFlag && state.dataHVACMgr->MyEnvrnFlag2) {
// Following comment is incorrect! (LKL) Even the first time through this does more than read in data.
// Zone equipment data needs to be read in before air loop data to allow the
Expand Down
38 changes: 37 additions & 1 deletion src/EnergyPlus/HeatBalanceSurfaceManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
#include <EnergyPlus/DataLoopNode.hh>
#include <EnergyPlus/DataMoistureBalance.hh>
#include <EnergyPlus/DataMoistureBalanceEMPD.hh>
#include <EnergyPlus/DataPhotovoltaics.hh>
#include <EnergyPlus/DataRuntimeLanguage.hh>
#include <EnergyPlus/DataSizing.hh>
#include <EnergyPlus/DataSurfaces.hh>
Expand Down Expand Up @@ -111,6 +112,7 @@
#include <EnergyPlus/OutputProcessor.hh>
#include <EnergyPlus/OutputReportPredefined.hh>
#include <EnergyPlus/OutputReportTabular.hh>
#include <EnergyPlus/Photovoltaics.hh>
#include <EnergyPlus/Psychrometrics.hh>
#include <EnergyPlus/ScheduleManager.hh>
#include <EnergyPlus/SolarShading.hh>
Expand Down Expand Up @@ -162,6 +164,17 @@ void ManageSurfaceHeatBalance(EnergyPlusData &state)
}
InitSurfaceHeatBalance(state); // Initialize all heat balance related parameters

// Surface-coupled PV must be initialized before its first temperature-dependent calculation.
if (state.dataPhotovoltaicState->GetInputFlag &&
state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "Generator:Photovoltaic") > 0) {
Photovoltaics::GetPVInput(state);
state.dataPhotovoltaicState->GetInputFlag = false;
}

Comment on lines +167 to +173

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not proposing we change this now, just wondering why not integrate this with InitSuraceHeatBalance ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's a good point. It might fit better there, but maybe the thinking was it's only for PV not for "all surfaces" and so maybe it doesn't?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think the idea is that InitSurfaceHeatBalance does the heat balance initialization whereas this block is related to making sure PV data are available right before running SimSurfaceCoupledPV (i.e., not doing any surface heat balancing).

for (int PVnum = 1; PVnum <= state.dataPhotovoltaic->NumPVs; ++PVnum) {
Photovoltaics::SimSurfaceCoupledPV(state, PVnum);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hitting this before resolving the outside and inside heat balance. Seems fine.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think it's actually necessary, and is the crucial piece in this PR. We need to supply the PV heat sink that the surface heat balance eqns need for the current timestep.

}

// Solve the zone heat balance 'Detailed' solution
// Call the outside and inside surface heat balances
if (state.dataHeatBalSurfMgr->ManageSurfaceHeatBalancefirstTime) {
Expand Down Expand Up @@ -231,6 +244,26 @@ void ManageSurfaceHeatBalance(EnergyPlusData &state)
state.dataHeatBalSurfMgr->ManageSurfaceHeatBalancefirstTime = false;
}

void ResimulateSurfaceHeatBalanceForPV(EnergyPlusData &state)
{
// Repeat the coupled surface and PV calculations after electric simulation changes the PV heat sink.
if (!state.dataHVACGlobal->PVSurfaceHeatBalanceResimFlag) {
return;
}

for (int pass = 1; pass <= 2; ++pass) {
state.dataHVACGlobal->PVSurfaceHeatBalanceResimFlag = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

So, you set PVSurfaceHeatBalanceResimFlag to false, and then inside SimSurfaceCoupledPV it can drop into UpdatePVIntegrationSource, which then evaluates whether another iteration is needed. But only up to a max of 2. Seems OK, I think.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the pass max of 2 due to the impact on the outside surface heat balance and needing it to better update?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Here we are within an HVAC iteration. We want to limit each HVAC iteration to two surface/PV correction updates:

  • pass=1: apply the current PV sink, then recalculate PV from updated surface state
  • pass=2: apply that recalculated sink, then recalculate PV again

If after 2 passes PV surface sink is still changing, then the HVAC iteration loop can hit ResimulateSurfaceHeatBalanceForPV again (after zone demand, etc is recomputed).

CalcHeatBalanceOutsideSurf(state);
CalcHeatBalanceInsideSurf(state);
for (int PVnum = 1; PVnum <= state.dataPhotovoltaic->NumPVs; ++PVnum) {
Photovoltaics::SimSurfaceCoupledPV(state, PVnum);
}
if (!state.dataHVACGlobal->PVSurfaceHeatBalanceResimFlag) {
break;
}
}
}
Comment on lines +247 to +265

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

New function to resim the outside and and inside surface heat balances, and then (?) resim the PV after. That's backwards from what you did earlier. Does it matter?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't it true that by the time the simulation has hit this point that it has already gone through the inside and outside heat balance? So, why would it need to do another round of that (for all surfaces) if the point here is that something in the PV systems has to update and another round of the heat balance has to take place? In UpdateFinalSurfaceHeatBalance, another round is needed because those systems are running at the HVAC time step and the zone heat balance needs to update based on the "average" of what has been happening. Since PV is running at the HVAC time step level, is something similar happening here? Or is there a different reason that I'm not understanding yet? I guess, why not call SimSurfaceCoupledPV and then just use the UpdateFinalSurfaceHeatBalance with an updated flag?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@mitchute The differing orders are intentional:

  • Initial pass: PV first, because there is no current PV sink yet
  • Corrective pass: surface first, because there is already a sink waiting to be applied

@RKStrand I think we could use UpdateFinalSurfaceHeatBalance with an updated flag, but then we wouldn't be able to feed the corrected surface temperatures back through PredictSystemLoads and zone equipment (since ManageAirHeatBalance occurs before UpdateFinalSurfaceHeatBalance).


// Beginning Initialization Section of the Module
//******************************************************************************

Expand Down Expand Up @@ -5433,6 +5466,10 @@ void UpdateFinalSurfaceHeatBalance(EnergyPlusData &state)
// Call the outside and inside surface heat balances
CalcHeatBalanceOutsideSurf(state);
CalcHeatBalanceInsideSurf(state);

for (int PVnum = 1; PVnum <= state.dataPhotovoltaic->NumPVs; ++PVnum) {
Photovoltaics::SimSurfaceCoupledPV(state, PVnum);
}
}
}

Expand Down Expand Up @@ -7238,7 +7275,6 @@ void CalcHeatBalanceOutsideSurf(EnergyPlusData &state,
// // Locals
// // SUBROUTINE ARGUMENT DEFINITIONS:
//
//>>>>>>> origin/develop
// SUBROUTINE PARAMETER DEFINITIONS:
constexpr std::string_view RoutineNameGroundTemp("CalcHeatBalanceOutsideSurf:GroundTemp");
constexpr std::string_view RoutineNameGroundTempFC("CalcHeatBalanceOutsideSurf:GroundTempFC");
Expand Down
2 changes: 2 additions & 0 deletions src/EnergyPlus/HeatBalanceSurfaceManager.hh
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ namespace HeatBalanceSurfaceManager {

void ManageSurfaceHeatBalance(EnergyPlusData &state);

void ResimulateSurfaceHeatBalanceForPV(EnergyPlusData &state);

// Beginning Initialization Section of the Module
//******************************************************************************

Expand Down
87 changes: 77 additions & 10 deletions src/EnergyPlus/Photovoltaics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ namespace Photovoltaics {
} break;
}

auto const cellIntegrationMode = state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode;
if (cellIntegrationMode == CellIntegration::SurfaceOutsideFace || cellIntegrationMode == CellIntegration::TranspiredCollector ||
cellIntegrationMode == CellIntegration::ExteriorVentedCavity || cellIntegrationMode == CellIntegration::PVTSolarCollector) {
// Surface coupling runs before the electric generator pass, so retain the current schedule state for the next coupling pass.
state.dataPhotovoltaic->PVarray(PVnum).SurfaceCouplingRunFlag = RunFlag;
}

ReportPV(state, PVnum);
}

Expand Down Expand Up @@ -841,7 +848,6 @@ namespace Photovoltaics {

// Using/Aliasing
Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
using TranspiredCollector::SetUTSCQdotSource;

// SUBROUTINE LOCAL VARIABLE DECLARATIONS:
int thisZone; // working index for zones
Expand All @@ -861,24 +867,85 @@ namespace Photovoltaics {

switch (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode) {
// SurfaceSink is not multiplied...
case CellIntegration::SurfaceOutsideFace: {
state.dataHeatBalFanSys->QPVSysSource(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr) =
-1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink;
} break;
case CellIntegration::SurfaceOutsideFace:
case CellIntegration::TranspiredCollector:
case CellIntegration::ExteriorVentedCavity:
case CellIntegration::PVTSolarCollector:
UpdatePVIntegrationSource(state, PVnum);
break;
default:
break;
}
}

void SimSurfaceCoupledPV(EnergyPlusData &state, int const PVnum)
{
// Recalculate PV that depends on a surface or collector temperature before the heat balance uses its current sink.
auto &pv = state.dataPhotovoltaic->PVarray(PVnum);
switch (pv.CellIntegrationMode) {
case CellIntegration::SurfaceOutsideFace:
case CellIntegration::TranspiredCollector:
case CellIntegration::ExteriorVentedCavity:
case CellIntegration::PVTSolarCollector:
break;
default:
return;
}

switch (pv.PVModelType) {
case PVModel::Simple:
CalcSimplePV(state, PVnum);
break;
case PVModel::TRNSYS:
InitTRNSYSPV(state, PVnum);
CalcTRNSYSPV(state, PVnum, pv.SurfaceCouplingRunFlag);
break;
case PVModel::Sandia:
CalcSandiaPV(state, PVnum, pv.SurfaceCouplingRunFlag);
break;
default:
break;
}

UpdatePVIntegrationSource(state, PVnum);
}

void UpdatePVIntegrationSource(EnergyPlusData &state, int const PVnum)
{
// Publish the PV sink to its coupled thermal model and request another pass when the sink changes materially.
auto &pv = state.dataPhotovoltaic->PVarray(PVnum);
Real64 const previousSource = pv.SurfaceCouplingSource;

switch (pv.CellIntegrationMode) {
case CellIntegration::SurfaceOutsideFace:
state.dataHeatBalFanSys->QPVSysSource(pv.SurfacePtr) = -pv.SurfaceSink;
break;
case CellIntegration::TranspiredCollector: {
SetUTSCQdotSource(state, state.dataPhotovoltaic->PVarray(PVnum).UTSCPtr, -1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink);
TranspiredCollector::SetUTSCQdotSource(state, pv.UTSCPtr, -1.0 * pv.SurfaceSink);
} break;
case CellIntegration::ExteriorVentedCavity: {
SetVentedModuleQdotSource(
state, state.dataPhotovoltaic->PVarray(PVnum).ExtVentCavPtr, -1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink);
SetVentedModuleQdotSource(state, pv.ExtVentCavPtr, -1.0 * pv.SurfaceSink);
} break;
case CellIntegration::PVTSolarCollector: {
PhotovoltaicThermalCollectors::SetPVTQdotSource(
state, state.dataPhotovoltaic->PVarray(PVnum).PVTPtr, -1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink);
PhotovoltaicThermalCollectors::SetPVTQdotSource(state, pv.PVTPtr, -1.0 * pv.SurfaceSink);
} break;
default:
break;
}

pv.SurfaceCouplingNeedsResim = std::abs(pv.SurfaceSink - previousSource) > 0.1;
pv.SurfaceCouplingSource = pv.SurfaceSink;
if (pv.SurfaceCouplingNeedsResim) {
state.dataHVACGlobal->SimElecCircuitsFlag = true;
if (pv.CellIntegrationMode == CellIntegration::SurfaceOutsideFace || pv.CellIntegrationMode == CellIntegration::ExteriorVentedCavity ||
pv.CellIntegrationMode == CellIntegration::TranspiredCollector) {
state.dataHVACGlobal->PVSurfaceHeatBalanceResimFlag = true;
}
if (pv.CellIntegrationMode == CellIntegration::TranspiredCollector || pv.CellIntegrationMode == CellIntegration::PVTSolarCollector) {
state.dataHVACGlobal->SimAirLoopsFlag = true;
state.dataHVACGlobal->SimPlantLoopsFlag = true;
}
}
}

// *************
Expand Down
4 changes: 4 additions & 0 deletions src/EnergyPlus/Photovoltaics.hh
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ namespace Photovoltaics {

void CalcSimplePV(EnergyPlusData &state, int const thisPV);

void SimSurfaceCoupledPV(EnergyPlusData &state, int const PVnum);

void UpdatePVIntegrationSource(EnergyPlusData &state, int const PVnum);

void ReportPV(EnergyPlusData &state, int const PVnum);

// *************
Expand Down
52 changes: 52 additions & 0 deletions tst/EnergyPlus/unit/HeatBalanceSurfaceManager.unit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include <EnergyPlus/DataHeatBalance.hh>
#include <EnergyPlus/DataLoopNode.hh>
#include <EnergyPlus/DataMoistureBalance.hh>
#include <EnergyPlus/DataPhotovoltaics.hh>
#include <EnergyPlus/DataRuntimeLanguage.hh>
#include <EnergyPlus/DataSizing.hh>
#include <EnergyPlus/DataSurfaces.hh>
Expand All @@ -79,6 +80,7 @@
#include <EnergyPlus/Material.hh>
#include <EnergyPlus/OutAirNodeManager.hh>
#include <EnergyPlus/OutputReportTabular.hh>
#include <EnergyPlus/Photovoltaics.hh>
#include <EnergyPlus/ScheduleManager.hh>
#include <EnergyPlus/SolarShading.hh>
#include <EnergyPlus/SurfaceGeometry.hh>
Expand Down Expand Up @@ -2805,6 +2807,15 @@ TEST_F(EnergyPlusFixture, HeatBalanceSurfaceManager_TestSurfTempCalcHeatBalanceA
" A1 - 1 IN STUCCO, !- Outside Layer",
" GP01; !- Layer 3",

" ConstructionProperty:InternalHeatSource,",
" PV Source, !- Name",
" EXTWALL:LIVING, !- Construction Name",
" 1, !- Thermal Source Present After Layer Number",
" 1, !- Temperature Calculation Requested After Layer Number",
" 1, !- Dimensions for the CTF Calculation",
" 0.1524, !- Tube Spacing {m}",
" 0.0; !- Two-Dimensional Temperature Calculation Position",

" Construction,",
" FLOOR:LIVING, !- Name",
" CC03, !- Outside Layer",
Expand Down Expand Up @@ -2976,6 +2987,9 @@ TEST_F(EnergyPlusFixture, HeatBalanceSurfaceManager_TestSurfTempCalcHeatBalanceA
SurfaceGeometry::SetupZoneGeometry(*state, ErrorsFound);
EXPECT_FALSE(ErrorsFound);

// IsUsedCTF is only set once surfaces are set up, so CTFs can only be calculated after SetupZoneGeometry.
HeatBalanceManager::InitConductionTransferFunctions(*state);

// Clear schedule type warnings
EXPECT_TRUE(has_err_output(true));

Expand Down Expand Up @@ -3076,6 +3090,44 @@ TEST_F(EnergyPlusFixture, HeatBalanceSurfaceManager_TestSurfTempCalcHeatBalanceA
EXPECT_EQ(-0.1, state->dataHeatBalSurf->SurfQAdditionalHeatSourceOutside(1));
CalcHeatBalanceInsideSurf(*state);
EXPECT_EQ(0.1, state->dataHeatBalSurf->SurfQAdditionalHeatSourceInside(6));

// Apply a PV sink after the initial surface pass, as happens when PV is recalculated during HVAC simulation.
auto const surfaceTemperatureBeforePV = state->dataHeatBalSurf->SurfTempOut(1);
state->dataHeatBal->AnyInternalHeatSourceInInput = true;
// SurfQsrcHist is only allocated when AnyInternalHeatSourceInInput is set, so allocate it before reading it below.
state->dataHeatBalSurf->SurfQsrcHist.dimension(state->dataSurface->TotSurfaces, Construction::MaxCTFTerms, 0.0);
auto const sourceHistoryBeforePV = state->dataHeatBalSurf->SurfQsrcHist(1, 1);
state->dataHeatBalFanSys->QPVSysSource.dimension(state->dataSurface->TotSurfaces, 0.0);
state->dataHeatBalFanSys->QPVSysSource(1) = -100.0;
state->dataHVACGlobal->PVSurfaceHeatBalanceResimFlag = true;

// The resimulation must consume the request and incorporate the PV sink into the surface balance.
ResimulateSurfaceHeatBalanceForPV(*state);

EXPECT_FALSE(state->dataHVACGlobal->PVSurfaceHeatBalanceResimFlag);
EXPECT_NE(sourceHistoryBeforePV, state->dataHeatBalSurf->SurfQsrcHist(1, 1));
EXPECT_NE(surfaceTemperatureBeforePV, state->dataHeatBalSurf->SurfTempOut(1));

// A source change on the first coupled pass followed by a stable second pass is converged and
// must not leave a stale request for another outer HVAC iteration.
state->dataPhotovoltaic->PVarray.allocate(1);
state->dataPhotovoltaic->NumPVs = 1;
auto &pv = state->dataPhotovoltaic->PVarray(1);
pv.PVModelType = DataPhotovoltaics::PVModel::Simple;
pv.CellIntegrationMode = DataPhotovoltaics::CellIntegration::SurfaceOutsideFace;
pv.SurfacePtr = 1;
pv.SimplePVModule.EfficencyInputMode = DataPhotovoltaics::Efficiency::Fixed;
pv.SimplePVModule.AreaCol = 1.0;
pv.SimplePVModule.PVEfficiency = 0.1;
pv.SurfaceCouplingSource = 0.0;
state->dataHeatBal->SurfQRadSWOutIncident(1) = 1000.0;
state->dataHVACGlobal->PVSurfaceHeatBalanceResimFlag = true;

ResimulateSurfaceHeatBalanceForPV(*state);

EXPECT_DOUBLE_EQ(100.0, pv.SurfaceCouplingSource);
EXPECT_FALSE(pv.SurfaceCouplingNeedsResim);
EXPECT_FALSE(state->dataHVACGlobal->PVSurfaceHeatBalanceResimFlag);
}

TEST_F(EnergyPlusFixture, HeatBalanceSurfaceManager_TestReportIntMovInsInsideSurfTemp)
Expand Down
Loading
Loading