Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
80 changes: 80 additions & 0 deletions design/FY2026/NFP-Dewpoint_Humidistat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
Dew-point Temperature Humidistat
================

**Jeremy Lerond, Pacific Northwest National Laboratory**

- Original Date: 03/03/2026
- Revision Date: 03/03/2026


## Justification for New Feature ##

Currently, EnergyPlus humidistats are used to control zone humidity using relative humidity (RH). However, in practice it is also common for thermostats/humidity controllers to control (or limit) humidity using dew-point temperature. In addition, standards such as ASHRAE 62.1 include requirements—under certain conditions—that the zone (space) dew-point temperature must not exceed a specified limit (see Addendum k to ASHRAE 62.1–2022).

This feature will allow users to specify a maximum/minimum allowable space dew-point temperature (i.e., a dew-point setpoint) to trigger humidity control.

While controlling humidity based on dew-point is currently possible in EnergyPlus using EMS programs, it requires having multiple programs (or a large one) to recalculate each zone's relative humidity setpoint based on the targeted dew-point temperature. This new feature will provide a more streamlined and user-friendly way to control humidity based on dew-point temperature.

## E-mail and Conference Call Conclusions ##

N/A

## Overview ##

The `ZoneControl:Humidistat` object uses both a humidifying and dehumidifying setpoint schedule based on RH. The new features will add a new `Control Variable` input field to specify what variable is described in the schedule. The value for this new input would be `RelativeHumidity` (current approach) and `Dew-point Temperature` (new approach).

## Approach ##

First, the existing schedule fields would be renamed as follows:
- `Humidifying Relative Humidity Setpoint Schedule Name` -> `Humidifying Setpoint Schedule Name`
- `Dehumidifying Relative Humidity Setpoint Schedule Name` -> `Dehumidifying Setpoint Schedule Name`

Second, a new input field would be added to the `ZoneControl:Humidistat` object: `Control Variable`.

Third, that control variable would be used to determine In `calcPredictedCorrector` the current schedule inputs are used to calculate `WZoneSetPoint` which is the zone humidity setpoint. The new schedules would be used to also calculate `WZoneSetpoint` but based on a dew-point temperature target rather than a relative humidity target. The user could mix/match schedules to control zone humidity, for instance, use a dew-point-based dehumidification schedule and a relative humidity-based humidification schedule.

## Testing/Validation/Data Sources ##

A unit test will be included to test the new feature.

## Input Output Reference Documentation ##

The following field description would be added to the input/output reference guide:

```latex
\paragraph{Field: Control Variable}\label{field-humidistat-control-variable}

This field describes if the schedules describe relative humidity or dew-point temperature values.
```

## Input Description ##

The following fields would be added to the `ZoneControl:Humidistat` object:

```
ZoneControl:Humidistat,
[...]
A5 ; \field Control Variable
\note When using RelativeHumidity, the schedule values should be in percentages.
\type choice
\key RelativeHumidity
\key Dewpoint
\default RelativeHumidity
```

## Outputs Description ##

No new outputs will be added.

## Engineering Reference ##

This new feature doesn't require any engineering reference changes.

## Example File and Transition Changes ##

This new feature doesn't require any transition changes.

## References ##

- ASHRAE Standard 62.1-2022 Addendum k, https://www.ashrae.org/file%20library/technical%20resources/standards%20and%20guidelines/standards%20errata/standards/62_1_2022_k_20240422.pdf
- Lennox S40 Smart Thermostat, https://www.lennox.com/dA/6003ce4d6f/508223-03b.pdf
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ \subsubsection{Inputs}\label{inputs-7-030}

\subsection{ZoneControl:Humidistat}\label{zonecontrolhumidistat}

The humidistat zone control object is used to control a zone to a single relative humidity setpoint schedule, or to dual humidity setpoint schedules (humidifying/ dehumidifying setpoints with deadband). The single setpoint humidistat requires single setpoint input only, and the dual-setpoint humidistat requires inputs of both humidifying and dehumidifying setpoints. The schedules consist of relative humidities, expressed as a percentage (0-100), to be used during the simulation for that zone's moisture prediction calculation. Only one humidistat control statement can be specified for each zone. For a single setpoint humidistat, if the zone relative humidity is below the control relative humidity setpoint and the equipment specified can humidify then that equipment will try and meet the zone's humidification load. The opposite is true if the zone relative humidity is above the control relative humidity setpoint and the equipment can dehumidify. For a dual setpoint humidistat, if the zone relative humidity is below the \textbf{humidifying} relative humidity setpoint and the equipment specified can humidify then that equipment will try and meet the zone's humidification load. The opposite is true if the zone relative humidity is above the \textbf{dehumidifying} relative humidity setpoint and the equipment can dehumidify.
The humidistat zone control object is used to control a zone to a single relative humidity setpoint schedule, or to dual humidity setpoint schedules (humidifying / dehumidifying setpoints with deadband). The single setpoint humidistat requires single setpoint input only, and the dual-setpoint humidistat requires inputs of both humidifying and dehumidifying setpoints. The schedules consist of relative humidities or dew-point temperatures, expressed as a percentage (0-100) or dew-point temperature, to be used during the simulation for that zone's moisture prediction calculation. Only one humidistat control statement can be specified for each zone. For a single setpoint humidistat, if the zone relative humidity or dew-point temperature is below the control relative humidity or dew-point temperature setpoint and the equipment specified can humidify then that equipment will try and meet the zone's humidification load. The opposite is true if the zone relative humidity or dew-point temperature is above the control relative humidity or dew-point temperature setpoint and the equipment can dehumidify. For a dual setpoint humidistat, if the zone relative humidity or dew-point temperature is below the \textbf{humidifying} relative humidity or dew-point temperature setpoint and the equipment specified can humidify then that equipment will try and meet the zone's humidification load. The opposite is true if the zone relative humidity or dew-point temperature is above the \textbf{dehumidifying} relative humidity or dew-point temperature setpoint and the equipment can dehumidify.

If the ZoneControl:Humidistat is used by a furnace or unitary system (Ref. Furnace and Unitary Systems) no other objects are required. The signal from the humidistat is used directly by that component. If the Zone Control:Humidistat object is used to control a Humidifier or used in conjunction with a \hyperref[controllerwatercoil]{Controller:WaterCoil} object with control variable \textbf{TemperatureAndHumidityRatio} or \textbf{HumidityRatio}, the following objects are required to determine a setpoint for those components for a single setpoint humidistat:

Expand Down Expand Up @@ -801,24 +801,29 @@ \subsubsection{Inputs}\label{inputs-8-028}

Name of the zone that is being controlled.

\paragraph{Field: Humidifying Relative Humidity Setpoint Schedule Name}\label{field-humidifying-relative-humidity-setpoint-schedule-name}
\paragraph{Field: Humidifying Setpoint Schedule Name}\label{field-humidifying-setpoint-schedule-name}

Name of a schedule that defines the humidifying relative humidity setpoint, expressed as a percentage (0-100), for each timestep of the simulation.
Name of a schedule that defines the humidifying relative humidity (expressed as a percentage (0-100)) or dew-point temperature setpoint for each timestep of the simulation.

\textbf{Note}: If only a single setpoint humidistat is desired, then input the single schedule name in the Humidifying Setpoint Schedule Name field (and leave the Dehumidifying Setpoint Schedule Name blank).

\paragraph{Field: Dehumidifying Relative Humidity Setpoint Schedule Name}\label{field-dehumidifying-relative-humidity-setpoint-schedule-name-1}
\paragraph{Field: Dehumidifying Setpoint Schedule Name}\label{field-dehumidifying-setpoint-schedule-name-1}

Name of a schedule that defines the dehumidifying relative humidity setpoint, expressed as a percentage (0-100), for each timestep of the simulation. This field is optional, only used if a dual setpoint humidistat is to be modeled.
Name of a schedule that defines the dehumidifying relative humidity setpoint (expressed as a percentage (0-100)) or dew-point temperature for each timestep of the simulation. This field is optional, only used if a dual setpoint humidistat is to be modeled.

\paragraph{Field: Control Variable}\label{field-humidistat-control-variable}

This fields describes if the schedules describe relative humidity or dew-point temperature values.

An example of this statement in an IDF is:

\begin{lstlisting}
ZoneControl:Humidistat,
Zone 2 Humidistat, !- Humidistat Name
EAST ZONE, !- Zone Name
Min Rel Hum Set Sch, !- Humidifying Relative Humidity Setpoint SCHEDULE Name
Max Rel Hum Set Sch; !- Dehumidifying Relative Humidity Setpoint SCHEDULE Name
Min Rel Hum Set Sch, !- Humidifying Setpoint Schedule Name
Max Rel Hum Set Sch, !- Dehumidifying Setpoint Schedule Name
RelativeHumidity; !- Control Variable
\end{lstlisting}

An example schedule for the Zone Control:Humidistat
Expand Down
14 changes: 10 additions & 4 deletions idd/Energy+.idd.in
Original file line number Diff line number Diff line change
Expand Up @@ -35261,15 +35261,21 @@ ZoneControl:Humidistat,
\required-field
\type object-list
\object-list ZoneNames
A3 , \field Humidifying Relative Humidity Setpoint Schedule Name
A3 , \field Humidifying Setpoint Schedule Name
\required-field
\note hourly schedule values should be in Relative Humidity (percent)
Copy link
Copy Markdown
Collaborator

@rraustad rraustad Apr 7, 2026

Choose a reason for hiding this comment

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

Maybe keep the notes and add "or Dew Point (C) based on the Control Variable field." That would also send home that both schedules need to have the same units.

\note Hourly schedule values should be in relative humidity (percent) or dew-point temperatures (deg. C).
\type object-list
\object-list ScheduleNames
A4 ; \field Dehumidifying Relative Humidity Setpoint Schedule Name
\note hourly schedule values should be in Relative Humidity (percent)
A4 , \field Dehumidifying Setpoint Schedule Name
\note Hourly schedule values should be in relative humidity (percent) or dew-point temperatures (deg. C).
\type object-list
\object-list ScheduleNames
A5 ; \field Control Variable
\note When using RelativeHumidity, the schedule values should be in percentages.
\type choice
\key RelativeHumidity
\key Dewpoint
\default RelativeHumidity

ZoneControl:Thermostat,
\memo Define the Thermostat settings for a zone or list of zones.
Expand Down
26 changes: 19 additions & 7 deletions src/EnergyPlus/DataZoneControls.hh
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ namespace DataZoneControls {
constexpr std::array<std::string_view, (int)TempCtrl::Num> tempCtrlNames = {"None", "Constant", "Scheduled"};
constexpr std::array<std::string_view, (int)TempCtrl::Num> tempCtrlNamesUC = {"NONE", "CONSTANT", "SCHEDULED"};

enum class HumidityCtrlVarType
{
Invalid = -1,
DewPoint,
RelativeHumidity,
Num
};

static constexpr std::array<std::string_view, (int)HumidityCtrlVarType::Num> humidityCtrlVarTypeNames = {"Dewpoint", "RelativeHumidity"};
static constexpr std::array<std::string_view, (int)HumidityCtrlVarType::Num> humidityCtrlVarTypeNamesUC = {"DEWPOINT", "RELATIVEHUMIDITY"};

struct TempSetptType
{
std::string Name;
Expand Down Expand Up @@ -159,13 +170,14 @@ namespace DataZoneControls {
std::string ControlName; // Name of this humidity controller
std::string ZoneName; // Name of the zone
int ActualZoneNum;
Sched::Schedule *humidifyingSched = nullptr; // humidifying schedule
Sched::Schedule *dehumidifyingSched = nullptr; // dehumidifying schedule
int ErrorIndex; // Error index when LowRH setpoint > HighRH setpoint
bool EMSOverrideHumidifySetPointOn; // EMS is calling to override humidifying setpoint
Real64 EMSOverrideHumidifySetPointValue; // value EMS is directing to use for humidifying setpoint
bool EMSOverrideDehumidifySetPointOn; // EMS is calling to override dehumidifying setpoint
Real64 EMSOverrideDehumidifySetPointValue; // value EMS is directing to use for dehumidifying setpoint
Sched::Schedule *humidifyingSched = nullptr; // humidifying schedule
Sched::Schedule *dehumidifyingSched = nullptr; // dehumidifying schedule
HumidityCtrlVarType humidityControlVariableType = HumidityCtrlVarType::RelativeHumidity; // Control variable type
int ErrorIndex; // Error index when LowRH setpoint > HighRH setpoint
bool EMSOverrideHumidifySetPointOn; // EMS is calling to override humidifying setpoint
Real64 EMSOverrideHumidifySetPointValue; // value EMS is directing to use for humidifying setpoint
bool EMSOverrideDehumidifySetPointOn; // EMS is calling to override dehumidifying setpoint
Real64 EMSOverrideDehumidifySetPointValue; // value EMS is directing to use for dehumidifying setpoint

// Default Constructor
ZoneHumidityControls()
Expand Down
24 changes: 22 additions & 2 deletions src/EnergyPlus/ZoneTempPredictorCorrector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,10 @@ void GetZoneAirSetPoints(EnergyPlusData &state)
ErrorsFound = true;
}

// Control variable
humidControlledZone.humidityControlVariableType =
static_cast<DataZoneControls::HumidityCtrlVarType>(getEnumValue(DataZoneControls::humidityCtrlVarTypeNamesUC, s_ipsc->cAlphaArgs(5)));

} // HumidControlledZoneNum

// Start to read Thermal comfort control objects
Expand Down Expand Up @@ -3491,8 +3495,24 @@ void ZoneSpaceHeatBalanceData::calcPredictedHumidityRatio(EnergyPlusData &state,
if (thisZone.humidityControlZoneIndex > 0) {
auto &humidityControlZone = state.dataZoneCtrls->HumidityControlZone(thisZone.humidityControlZoneIndex);
assert(humidityControlZone.ActualZoneNum == zoneNum);
ZoneRHHumidifyingSetPoint = humidityControlZone.humidifyingSched->getCurrentVal();
ZoneRHDehumidifyingSetPoint = humidityControlZone.dehumidifyingSched->getCurrentVal();
if (humidityControlZone.humidityControlVariableType == DataZoneControls::HumidityCtrlVarType::RelativeHumidity) {
ZoneRHHumidifyingSetPoint = humidityControlZone.humidifyingSched->getCurrentVal();
ZoneRHDehumidifyingSetPoint = humidityControlZone.dehumidifyingSched->getCurrentVal();
} else if (humidityControlZone.humidityControlVariableType ==
DataZoneControls::HumidityCtrlVarType::DewPoint) { // Recalculate RH setpoint from DP
ZoneRHHumidifyingSetPoint =
100 * Psychrometrics::PsyRhFnTdbWPb(
state,
this->ZT,
Psychrometrics::PsyWFnTdpPb(state, humidityControlZone.humidifyingSched->getCurrentVal(), state.dataEnvrn->OutBaroPress),
state.dataEnvrn->OutBaroPress);
ZoneRHDehumidifyingSetPoint =
100 * Psychrometrics::PsyRhFnTdbWPb(
state,
this->ZT,
Psychrometrics::PsyWFnTdpPb(state, humidityControlZone.dehumidifyingSched->getCurrentVal(), state.dataEnvrn->OutBaroPress),
state.dataEnvrn->OutBaroPress);
}
Comment on lines +3498 to +3515
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.

Use the less "invasive" approach; If a dew-point setpoint is used convert to RH since the existing approach uses RH.


// Apply EMS values to overwrite the humidistat values
if (humidityControlZone.EMSOverrideHumidifySetPointOn) {
Expand Down
1 change: 1 addition & 0 deletions testfiles/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ add_simulation_test(IDF_FILE HeatPumpWaterToAirEquationFit.idf EPW_FILE USA_IL_C
add_simulation_test(IDF_FILE HeatPumpWaterToAirWithAntifreezeAndLatentModel.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw)
add_simulation_test(IDF_FILE HeatPumpWaterToAirWithAntifreezeAndLatentModel2.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw)
add_simulation_test(IDF_FILE HeatPumpWaterToAirWithRHControl.idf EPW_FILE USA_FL_Miami.Intl.AP.722020_TMY3.epw)
add_simulation_test(IDF_FILE HeatPumpWaterToAirWithHumControlDewPoint.idf EPW_FILE USA_FL_Miami.Intl.AP.722020_TMY3.epw)
add_simulation_test(IDF_FILE HeatPumpwithBiquadraticCurves.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw)
add_simulation_test(IDF_FILE HeatRecoveryElectricChiller.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw)
add_simulation_test(IDF_FILE HeatRecoveryPlantLoop.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw)
Expand Down
Loading
Loading