NREL / EnergyPlus

EnergyPlus™ is a whole building energy simulation program that engineers, architects, and researchers use to model both energy consumption and water use in buildings.
https://energyplus.net
Other
1.13k stars 389 forks source link

Incorrect solar heat gain for opaque surfaces w/ `SurfaceProperty:IncidentSolarMultiplier` object #10001

Closed shorowit closed 1 year ago

shorowit commented 1 year ago

Issue overview

I am testing the SurfaceProperty:IncidentSolarMultiplier object and found that when I set the solar multiplier to zero for all windows, opaque surfaces (e.g., floors, internal mass) still show large values for Surface Inside Face Solar Radiation Heat Gain Energy whenever the sun is up.

I took the 1ZoneEvapCooler_4Win_incidentSolarMultiplier.idf test file, changed all multipliers to zero, and added another output variable for Surface Inside Face Solar Radiation Heat Gain Energy: 1ZoneEvapCooler_4Win_incidentSolarMultiplierZero.idf.txt

Here's an example of the floor solar radiation heat gain on the first day of the year for multipliers of zero (full shade) and one (full sun): image

As you can see, the solar heat gain on the floor is reduced, but not all the way to zero.

Details

Some additional details for this issue (if relevant):

Checklist

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

shorowit commented 1 year ago

@yujiex Any idea what the issue here might be?

yujiex commented 1 year ago

Let me look into it @shorowit

yujiex commented 1 year ago

Hi @shorowit, currently the incident solar multiplier is only applicable for windows, not opaque surfaces. Maybe that's the reason?

shorowit commented 1 year ago

@yujiex The original motivation of this feature was to duplicate the DOE2 shading-fraction. The floor is an interior surface, the only solar that it sees is solar into the zone through windows,. It doesn't make any sense that the floor can still magically have solar heat gain when there is no solar radiation entering the zone.

I can imagine the incident solar multiplier applying to exterior opaque surfaces in the future, but it doesn't make any sense to me that you should have to apply a separate solar multiplier to interior opaque surfaces if you wanted to match the DOE2 capability. I don't think any user would expect that.

yujiex commented 1 year ago

@shorowit Sorry for the delay. I looked at the code and Engineering Reference again. I think the issue is with the calculation of beam solar entering the zone from exterior windows.

First I traced where the output variable corresponds to: "Surface Inside Face Solar Radiation Heat Gain Energy" corresponds to state.dataHeatBalSurf->SurfQRadSolarInReport(SurfNum) in the code.

it gets value from state.dataHeatBalSurf->SurfQdotRadSolarInRep(surfNum), which then gets value from state.dataHeatBalSurf->SurfQdotRadSolarInRepPerArea(surfNum), which is dependent on two variables, state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(surfNum) and state.dataHeatBalSurf->SurfQdotRadLightsInPerArea(surfNum). The former holds short wave radiation absorbed on inside of opaque surface. The latter is the "Short wave from Lights radiation absorbed on inside of opaque surface".

The former is more relevant so I looked at where that variable is modified.

1 state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(SurfNum) += 
        state.dataHeatBal->EnclSolQSWRad(solEnclosureNum) * AbsIntSurf;
2 state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(SurfNum) += state.dataHeatBalSurf->SurfOpaqInitialDifSolInAbs(SurfNum); 
3 state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(SurfNum) +=
        state.dataSurface->SurfOpaqAI(SurfNum) * currBeamSolar(SurfNum);

This corresponds to the Engineering Reference

image

The number 1 above is the first term in the equation. It is the interior diffuse radiation absorbed by opaque surfaces. Number 3 above is the second term in the equation. It corresponds to interior beam radiation absorbed by opaque surfaces. In the following, we'll look at diffused solar term and the beam solar term separately

For the diffused solar term

image image

The QS term is computed from Q_SW, which is calculated from QD term (diffuse solar radiation entering or originating in zone) and the other lighting-related internal gains.

This corresponds to here in the code

 sumSpaceQLTSW += state.dataHeatBal->spaceIntGain(spaceNum).QLTSW;
...
 state.dataHeatBal->EnclSolQSWRad(enclosureNum) = state.dataHeatBal->EnclSolQD(enclosureNum) + sumSpaceQLTSW;

Here QD is calculated

image

Corresponding to here in the code

 state.dataHeatBal->EnclSolQD(enclosureNum) = 
        state.dataHeatBal->EnclSolDB(enclosureNum) * state.dataEnvrn->BeamSolarRad +
        state.dataHeatBal->EnclSolDBSSG(enclosureNum) +
        state.dataHeatBal->EnclSolInitialDifSolReflW(enclosureNum);

The first and second terms are both "Diffuse Solar from beam reflected from interior surfaces", the second one uses a schedule.

state.dataHeatBal->EnclSolDB(enclosureNum) = BTOTZone - BABSZone;

In the documentation

image

The following are the code about how BTOTZone is calculated

 BTOTZone += TBmAll * SunLitFract * CosInc * state.dataSurface->Surface(SurfNum).Area * InOutProjSLFracMult; // [m2] corresponds to first term in the equation
...
 TBmAll = TBmBm + TBmDif;
...
 TBmBm = POLYF(CosInc, thisConstruct.TransSolBeamCoef);                            //[-]
...
W5LsqFit(CosPhiIndepVar, Tsol, 6, 1, 10, thisConstruct.TransSolBeamCoef)

The conclusion here is state.dataHeatBal->EnclSolDB(enclosureNum) * state.dataEnvrn->BeamSolarRad didn't apply the multiplier as the state.dataEnvrn->BeamSolarRad was multiplied to the zone total EnclSolDB. To fix it, we'll add the multiplier to adjust the TBmAll when computing BTOTZone.

BTOTZone += <multiplier> * TBmAll * SunLitFract * CosInc * state.dataSurface->Surface(SurfNum).Area * InOutProjSLFracMult

For the beam solar term

image
3 state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(SurfNum) +=
        state.dataSurface->SurfOpaqAI(SurfNum) * currBeamSolar(SurfNum);

For opaque surfaces, currBeamSolar(SurfNum) will not be adjusted as the multiplier is applied to exterior windows not opaque surfaces like the interior floor. The adjustment needs to be in state.dataSurface->SurfOpaqAI(SurfNum)

The engineering reference about how this is calculated is as follows

image

We can see the same pattern of BeamSolarRad * \sum_i TBm_i as the diffused solar term. In the code

 Real64 BTOTWinZone = TBm * SunLitFract * state.dataSurface->Surface(SurfNum).Area * CosInc * InOutProjSLFracMult; //[m2]
 ...
 state.dataSurface->SurfOpaqAI(FloorNum) +=
BTOTWinZone * state.dataSolarShading->SurfIntAbsFac(FloorNum) / state.dataSurface->Surface(FloorNum).Area; //[-]

We'll add an adjustment in the BTOTWinZone equation like this

Real64 BTOTWinZone = <multiplier> * TBm * SunLitFract * state.dataSurface->Surface(SurfNum).Area * CosInc * InOutProjSLFracMult;

I tested locally. With these modifications, the non-zero floor solar heat gain is gone.

shorowit commented 1 year ago

@yujiex Thanks so much for investigating this! I'm super excited that you have tracked down the issue and have a potential fix. If you create a branch, I'd be happy to test it.

yujiex commented 1 year ago

@shorowit Thanks. I just created a PR with the fix. Please review.