diff --git a/design/FY2026/NFP_DataCenter_LBL.md b/design/FY2026/NFP_DataCenter_LBL.md
new file mode 100644
index 00000000000..a4688529ddf
--- /dev/null
+++ b/design/FY2026/NFP_DataCenter_LBL.md
@@ -0,0 +1,236 @@
+NFP: Liquid-cooled Data Centers
+================
+
+**Yujie Xu, Kaiyu Sun, Tianzhen Hong, LBNL**
+
+ - Original Date: June 2026
+ - Revision Date: June 2026
+ - Status: Draft for Review
+
+
+## Justification for New Feature ##
+
+The data center industry is rapidly shifting toward higher adoption of liquid cooling technologies, often involving supply water temperatures between 80F and 120F. Currently, EnergyPlus lacks native support for water-cooled or liquid-cooled IT equipment. Users must rely on complex and inefficient workarounds combining the existing air-cooled IT equipment object with plant load profiles and Energy Management System (EMS) scripting. Previous modeling efforts, such as the MOSTCOOL project, had to rely on PlantComponent:UserDefined to link with external piping modules or use HeatExchanger:FluidToFluid to approximate a Coolant Distribution Unit (CDU). These workarounds are not robust, and relying on external modules defeats the goal of having a native, self-contained EnergyPlus solution.
+
+Furthermore, using simple load profiles like LoadProfile:Plant on the fluid side fails to generate an accurate electrical load for proper meter reporting and cannot dynamically monitor actual chip performance constraints. There is also no native capability to accurately model hybrid data centers that utilize a combination of liquid cooling for high-density chips and air cooling for the remaining components not on the liquid loop. A dedicated native liquid-cooled IT equipment object, paired with specific data center cooling coils (such as Coil:Cooling:ITE:ColdPlate), is required to accurately capture these distinct thermal dynamics—calculating the real-time heat split between the fluid and the zone air, and integrating that load directly and seamlessly into standard EnergyPlus plant loops.
+
+## E-mail and Conference Call Conclusions ##
+
+N/A
+
+## Overview ##
+
+Data center liquid cooling efficiently removes massive amounts of heat from high-density electronics by circulating a liquid capable of absorbing and transporting thermal energy much faster than traditional air systems. The continuous process begins at the chip level, where cold plates attached directly to processors capture heat and transfer it to the circulating coolant. This warmed liquid then flows through flexible tubes and safe, quick-disconnect valves into rack-level manifolds, which aggregate the fluid from multiple servers and route it to a Coolant Distribution Unit (CDU). The CDU acts as a vital bridge, utilizing a liquid-to-liquid heat exchanger to safely transfer the thermal energy from the isolated IT cooling loop into the building's primary water loop without the two fluids ever mixing. Finally, at the building level, large pumps send this heated facility water outside to heavy equipment like chillers, dry coolers, or cooling towers, where the collected heat is rejected into the atmosphere before the newly chilled water cycles back inside to repeat the process.
+
+
+
+Figure 1. Schematic of data center liquid cooling at chip, rack, and building level.
+
+This feature enhancement will add native liquid cooling modules to EnergyPlus to accurately model liquid-cooled and hybrid data centers. Liquid-cooled IT equipment is defined as systems cooled by a fluid other than air, such as water, glycol, or refrigerants. To manage these systems natively, the project will introduce a parent IT equipment object (ElectricEquipment:ITE:LiquidCooled) that calculates transient power consumption and scales rack-level loads using a multiplier. Rather than creating isolated internal piping networks, this parent object will pass its thermal load to new dedicated cooling coil objects, such as Coil:Cooling:ITE:ColdPlate or Coil:Cooling:ITE:UserDefined. These coils will connect directly to standard EnergyPlus plant loops. This allows standard HeatExchanger:FluidToFluid and pump objects to accurately represent the CDU, seamlessly integrating the IT equipment into the existing plant architecture.
+
+These new IT and coil components will replace the use of basic load profiles. They will utilize actual component-side heat transfer physics (such as overall thermal resistance and maximum allowable chip temperatures) to accurately calculate the required flow rate and return temperature of the liquid loop. Crucially, this enables native hybrid load splitting: the system dynamically calculates the fraction of server heat captured by the cold plates while the remainder is correctly rejected as an air stream load to the zone. Connecting these components directly to the plant loop architecture enables robust evaluation of heat recovery, cogeneration, and water consumption while supporting flexible controls for high-temperature cooling.
+
+Modeling a cold plate—which conducts heat away from the CPU and rejects it into a circulating liquid—can be approached using either detailed computational fluid dynamics (CFD) or reduced-order system models. Table 1 shows the two main calculation pathways and their key inputs. The proposed EnergyPlus implementation will utilize the System-Level lumped parameter approach.
+
+Table 1. Cold plate calculation pathways and key inputs
+
+| Category | Goal | Typical Software Tools | Key Inputs Required |
+| --- | --- | --- | --- |
+| Component-Level (CFD) | Optimize internal fins, channels, and pressure drop. | Ansys Fluent, Icepak, Star-CCM+, OpenFOAM, SimScale | Precise 3D geometry (CAD), material thermal properties, coolant fluid properties, boundary conditions (inlet flow, chip heat flux). |
+| System-Level (Lumped) | Simulate total cooling plant response, energy use, and safety. | Modelica (Buildings Library), Datacor Fathom/Impulse, EnergyPlus | Performance curves (thermal resistance vs mass flow rate), pressure drop coefficients (K), thermal mass/capacitance, total heat load. |
+
+## Approach ##
+
+The development approach focuses on adding a suite of native data center IT and cooling coil objects that integrate directly into the existing EnergyPlus plant loop architecture. By formatting the liquid cooling hardware as standard EnergyPlus coil objects, this approach leverages the robust, existing plant network solver, allowing users to model Coolant Distribution Units (CDUs) using standard HeatExchanger:FluidToFluid and pump objects. Figure 2 shows a schematic diagram of the added components and their relationships.
+
+
+
+Figure 2. Schematic diagram of the added components.
+
+At the rack level, a new ElectricEquipment:ITE:LiquidCooled parent object natively represents liquid-cooled data center IT equipment racks. This object calculates the total transient IT power load, scales the system using a multiplier for rapid block-modeling of identical racks, and references a specific cooling coil component (e.g., Coil:Cooling:ITE:ColdPlate). By acting as the parent, the IT object calculates the raw thermal load and passes it down to the coil to determine the physical heat transfer split between the liquid loop and the zone air.
+
+At the cooling coil level, the simulation establishes the absolute maximum physical cooling capacity based on the user-defined maximum allowable chip temperature, the real-time fluid inlet conditions provided by the plant loop, and the cold plate's overall thermal resistance (or heat transfer coefficient). During the simulation, the engine continuously scales the nominal thermal resistance using a bivariate modifier curve to account for changing system conditions like varying flow fractions and fluid temperatures. For standard single-phase systems, this physical heat transfer ceiling is calculated using the sensible heat capacity of the liquid and the combined solid and convective thermal resistances.
+
+The final heat transferred into the fluid is set as the smaller value between the targeted rack heat load and this dynamically calculated physical limit. Any remaining target heat that exceeds the cold plate capacity is mathematically diverted directly into the surrounding data hall as a sensible air load. This architecture establishes a seamless, physics-based load-splitting sequence where liquid cooling handles its maximum capable portion of the thermal load, and the uncaptured heat (spillover) acts as an air load on the zone.
+
+While single-phase cooling is the standard for most applications, the proposed Coil:Cooling:ITE:UserDefined object provides an extensible framework for complex or emerging technologies. Rather than hardcoding two-phase fluid properties into EnergyPlus, this object provides Energy Management System (EMS) hooks. Users can deploy custom scripts to calculate two-phase latent heat limits, Rear Door Heat Exchanger (RDHx) air-side interactions, or immersion cooling dynamics, and use EMS actuators to directly override the coil's plant node conditions. Table 2 shows the key fields of the added components.
+
+Table 2. Core fields of the new objects
+
+| Object or interface | Purpose | Key inputs/outputs | Notes |
+| --- | --- | --- | --- |
+| ElectricEquipment:ITE:LiquidCooled | Calculates transient server power consumption and passes the resulting thermal load to a specified cooling coil. | Zone name, operating schedule, design power input, multiplier, cooling coil object type, cooling coil name. | Uses a multiplier field for rapid scaling of identical racks. Acts as the parent object defining total heat generation. |
+| Coil:Cooling:ITE:ColdPlate | Defines the physical heat transfer between the IT equipment and the standard E+ plant loop for single-phase coolants. | Heat transfer input method (UA or thermal resistance), maximum allowable chip temperature, fluid inlet/outlet nodes, design pressure drop. | Establishes the maximum thermal ceiling, dictating when cooling capacity is maxed out and residual heat must spill over into the zone air. |
+| Coil:Cooling:ITE:UserDefined | Provides a hook for EMS/Python-driven workflows to override default heat transfer physics at the IT-to-coolant interface. | Fluid inlet/outlet nodes, EMS Program Calling Manager Name, Design Fluid Flow Rate. | Allows researchers to model proprietary two-phase boiling behaviors, RDHx, or immersion cooling while retaining standard E+ plant loop connectivity. |
+
+At a high level, the ElectricEquipment:ITE:LiquidCooled object evaluates the total IT load and passes this target heat load to the referenced Coil:Cooling:ITE:ColdPlate object. Simultaneously, the coil object checks the current fluid supply conditions from the facility plant loop. It calculates the maximum physical heat transfer possible given its thermal properties and the maximum allowable chip temperature. The simulation then evaluates whether the target IT load exceeds this physical maximum. If the cold plate has sufficient capacity, it absorbs the full target heat load; if the capacity is maxed out, the absorbed liquid load is capped at the physical limit.
+
+Any remaining heat, calculated as the total IT power minus the finalized liquid load, is diverted directly into the data center space as a sensible air load. Finally, the E+ plant loop automatically aggregates the heat and fluid flow from all connected cold plate coils. This aggregated hot fluid travels through the standard E+ plant pipe network to the secondary side of a HeatExchanger:FluidToFluid (representing the CDU), which then rejects the data center heat to the primary facility cooling plant.
+
+## Testing/Validation/Data Sources ##
+
+The feature will be tested and demonstrated with a test file derived from a baseline liquid-cooling data center model using a dry cooler as the cooling source, `1ZoneDataCenterCRAHandplant-liquidcooling-NoEMS-drycooler-2CDUs.idf`. Manual checks of the time-step EnergyPlus simulation results will be conducted to ensure the new components accurately calculate secondary loop performance compared to previous HeatExchanger:FluidToFluid approximations.
+
+## Input Output Reference Documentation ##
+
+N/A
+
+## Input Description ##
+
+The following new IDD objects will be added.
+
+```
+ElectricEquipment:ITE:LiquidCooled,
+ \memo Represents liquid-cooled data center IT equipment racks.
+ \memo Calculates power consumption and rejects heat to ITE cooling coils.
+ A1 , \field Name
+ \required-field
+ \type alpha
+ \reference ITEAndITEListNames
+ A2 , \field Zone or Space Name
+ \required-field
+ \type object-list
+ \object-list ZoneAndSpaceNames
+ \note Zone or Space the IT equipment is located in.
+ \note Spillover air heat will be rejected to this space's heat balance.
+ A3 , \field Availability Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ \note Availability schedule name for this equipment. Schedule value > 0 means the equipment is on.
+ \note If this field is blank, the equipment is always available.
+ A4 , \field Compute Load Schedule Name
+ \required-field
+ \type object-list
+ \object-list ScheduleNames
+ \note Defines the transient CPU loading schedule.
+ \note This 0-1 factor multiplied by the design power input is the current CPU power.
+ N1 , \field Design Power Input
+ \required-field
+ \type real
+ \units W
+ \note Max power consumption of a single IT equipment rack.
+ N2 , \field Multiplier
+ \type real
+ \default 1.0
+ \minimum 1.0
+ \note Scales power and heat to represent multiple identical racks.
+ N3 , \field Design Fan Power Input Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ \note Retained for auxiliary server fans contributing to air load.
+ A5 , \field IT Equipment Power Modifier Curve Name
+ \type object-list
+ \object-list BivariateFunctions
+ \note Modifies power based on loading and inlet temperature.
+ N4 , \field Liquid Heat Capture Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ \default 0.8
+ \note The fraction of the total ITE heat generation (CPU + Fan) that is removed by the liquid cooling loop at design conditions. The remaining fraction is transferred to the zone air.
+ A6 , \field Liquid Heat Capture Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ \note If provided, this schedule multiplies the Liquid Heat Capture Fraction field. This allows the capture effectiveness to vary dynamically during the simulation.
+ A7 , \field Cooling Coil 1 Object Type
+ \required-field
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ \note The type of the first cooling component handling physical heat transfer.
+ A8 , \field Cooling Coil 1 Name
+ \required-field
+ \type object-list
+ \object-list CoilCoolingITENames
+ \note The specific name of the first cooling component handling physical heat transfer.
+ N5 , \field Cooling Coil 1 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ \note A static fraction (0.0 to 1.0) of the total IT liquid load directed to this coil.
+ \note If this field is used, the Schedule Name field below should be left blank.
+ A9 , \field Cooling Coil 1 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ \note Schedule (0.0 to 1.0) defining the fraction of the total IT liquid load directed to this coil.
+ \note If this field is used, the static Load Fraction field above should be left blank.
+ A10, \field Cooling Coil 2 Object Type
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ \note The type of the second cooling component, if applicable (e.g., a secondary RDHx).
+ A11, \field Cooling Coil 2 Name
+ \type object-list
+ \object-list CoilCoolingITENames
+ N6 , \field Cooling Coil 2 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ A12, \field Cooling Coil 2 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ A13, \field Cooling Coil 3 Object Type
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ A14, \field Cooling Coil 3 Name
+ \type object-list
+ \object-list CoilCoolingITENames
+ N7 , \field Cooling Coil 3 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ A15, \field Cooling Coil 3 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ A16, \field Cooling Coil 4 Object Type
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ A17, \field Cooling Coil 4 Name
+ \type object-list
+ \object-list CoilCoolingITENames
+ N8 , \field Cooling Coil 4 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ A18; \field Cooling Coil 4 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+```
+
+## Outputs Description ##
+
+The outputs for `ElectricEquipment:ITE:LiquidCooled` are as follows
+```
+ Zone,Average,ITE CPU Electricity Rate [W]
+ Zone,Sum,ITE CPU Electricity Energy [J]
+ Zone,Average,ITE Fan Electricity Rate [W]
+ Zone,Sum,ITE Fan Electricity Energy [J]
+ Zone,Average,ITE Total Electricity Rate [W]
+ Zone,Sum,ITE Total Electricity Energy [J]
+ Zone,Average,ITE Total Heat Generation Rate [W]
+ Zone,Sum,ITE Total Heat Generation Energy [J]
+ Zone,Average,ITE Liquid Heat Capture Fraction []
+ Zone,Average,ITE Liquid Heat Gain Rate [W]
+ Zone,Sum,ITE Liquid Heat Gain Energy [J]
+ Zone,Average,ITE Air Heat Gain to Zone Rate [W]
+ Zone,Sum,ITE Air Heat Gain to Zone Energy [J]
+```
+
+The ITE CPU electricity rate and energy outputs represent the power and energy consumed specifically by the compute components of the IT equipment. The ITE fan electricity rate and energy outputs represent the power and energy consumed by the internal fans used to assist with thermal management within the equipment. The ITE total electricity rate and energy outputs represent the overall power and energy consumption of the liquid-cooled IT equipment, which is the sum of the CPU and fan electricity usage.
+
+The ITE total heat generation rate and energy outputs represent the entire thermal load produced by the equipment, which is inherently equal to the total electricity consumed. The ITE liquid heat capture fraction reports the final fraction of heat being routed to the liquid loop at the current timestep, accounting for the design input field and any modifying schedules. The ITE liquid heat gain rate and energy outputs report the amount of this total heat generation that is captured and removed directly by the attached liquid cooling loop. The ITE air heat gain to zone rate and energy outputs represent the remaining thermal fraction that is not captured by the liquid loop and is instead dissipated into the surrounding zone air as a sensible heat gain. In addition to these core object-level outputs, EnergyPlus will automatically generate corresponding Space and Zone level aggregations for every variable listed above.
+
+## Engineering Reference ##
+
+N/A
+
+## Example File and Transition Changes ##
+
+N/A
+
+## References ##
+
+N/A
diff --git a/design/FY2026/figure_1_datacenter_lbl.png b/design/FY2026/figure_1_datacenter_lbl.png
new file mode 100644
index 00000000000..d0799a3dff8
Binary files /dev/null and b/design/FY2026/figure_1_datacenter_lbl.png differ
diff --git a/design/FY2026/figure_2_datacenter_lbl.png b/design/FY2026/figure_2_datacenter_lbl.png
new file mode 100644
index 00000000000..2e3ddf5f139
Binary files /dev/null and b/design/FY2026/figure_2_datacenter_lbl.png differ
diff --git a/idd/Energy+.idd.in b/idd/Energy+.idd.in
index 39702d36293..86717f09228 100644
--- a/idd/Energy+.idd.in
+++ b/idd/Energy+.idd.in
@@ -23123,6 +23123,135 @@ ElectricEquipment:ITE:AirCooled,
\type object-list
\object-list ScheduleNames
+ElectricEquipment:ITE:LiquidCooled,
+ \memo Represents liquid-cooled data center IT equipment racks.
+ \memo Calculates power consumption and rejects heat to ITE cooling coils.
+ \min-fields 14
+ A1 , \field Name
+ \required-field
+ \type alpha
+ \reference ITEAndITEListNames
+ A2 , \field Zone or Space Name
+ \note ZoneList and SpaceList names are not allowed.
+ \required-field
+ \type object-list
+ \object-list ZoneNames
+ \object-list SpaceNames
+ \note Zone or Space the IT equipment is located in.
+ \note Spillover air heat will be rejected to this space's heat balance.
+ A3 , \field Availability Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ \note Availability schedule name for this equipment. Schedule value > 0 means the equipment is on.
+ \note If this field is blank, the equipment is always available.
+ A4 , \field Compute Load Schedule Name
+ \required-field
+ \type object-list
+ \object-list ScheduleNames
+ \note Defines the transient CPU loading schedule.
+ \note This 0-1 factor multiplied by the design power input is the current CPU power.
+ N1 , \field Design Power Input
+ \required-field
+ \type real
+ \units W
+ \minimum 0
+ \ip-units W
+ \note Max power consumption of a single IT equipment rack.
+ N2 , \field Multiplier
+ \type real
+ \default 1.0
+ \minimum 1.0
+ \note Scales power and heat to represent multiple identical racks.
+ N3 , \field Design Fan Power Input Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ \default 0.0
+ \note Retained for auxiliary server fans contributing to air load.
+ A5 , \field IT Equipment Power Modifier Curve Name
+ \type object-list
+ \object-list BivariateFunctions
+ \note Modifies power based on loading (x) and zone air temperature (y).
+ \note This curve (table) should equal 1.0 at design conditions.
+ \note If this field is left blank, the curve is assumed to always equal 1.0.
+ N4 , \field Liquid Heat Capture Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ \default 0.8
+ \note The fraction of the total ITE heat generation (CPU + Fan) that is removed by the liquid cooling
+ \note loop at design conditions. The remaining fraction is transferred to the zone air.
+ A6 , \field Liquid Heat Capture Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ \note If provided, this schedule multiplies the Liquid Heat Capture Fraction field. This allows the
+ \note capture effectiveness to vary dynamically during the simulation.
+ A7 , \field Cooling Coil 1 Object Type
+ \required-field
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ \note The type of the first cooling component handling physical heat transfer.
+ A8 , \field Cooling Coil 1 Name
+ \required-field
+ \type object-list
+ \object-list CoilCoolingITENames
+ \note The specific name of the first cooling component handling physical heat transfer.
+ N5 , \field Cooling Coil 1 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ \note A static fraction (0.0 to 1.0) of the total IT liquid load directed to this coil.
+ \note If this field is used, the Schedule Name field below should be left blank.
+ A9 , \field Cooling Coil 1 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ \note Schedule (0.0 to 1.0) defining the fraction of the total IT liquid load directed to this coil.
+ \note If this field is used, the static Load Fraction field above should be left blank.
+ A10, \field Cooling Coil 2 Object Type
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ \note The type of the second cooling component, if applicable (e.g., a secondary RDHx).
+ A11, \field Cooling Coil 2 Name
+ \type object-list
+ \object-list CoilCoolingITENames
+ N6 , \field Cooling Coil 2 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ A12, \field Cooling Coil 2 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ A13, \field Cooling Coil 3 Object Type
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ A14, \field Cooling Coil 3 Name
+ \type object-list
+ \object-list CoilCoolingITENames
+ N7 , \field Cooling Coil 3 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ A15, \field Cooling Coil 3 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+ A16, \field Cooling Coil 4 Object Type
+ \type choice
+ \key Coil:Cooling:ITE:ColdPlate
+ \key Coil:Cooling:ITE:UserDefined
+ A17, \field Cooling Coil 4 Name
+ \type object-list
+ \object-list CoilCoolingITENames
+ N8 , \field Cooling Coil 4 Load Fraction
+ \type real
+ \minimum 0.0
+ \maximum 1.0
+ A18; \field Cooling Coil 4 Load Fraction Schedule Name
+ \type object-list
+ \object-list ScheduleNames
+
ZoneBaseboard:OutdoorTemperatureControlled,
\memo Specifies outside temperature-controlled electric baseboard heating.
\memo If a ZoneList, SpaceList, or a Zone comprised of more than one Space is specified
diff --git a/src/EnergyPlus/DataHeatBalance.hh b/src/EnergyPlus/DataHeatBalance.hh
index 07c2df4014d..df0127c885c 100644
--- a/src/EnergyPlus/DataHeatBalance.hh
+++ b/src/EnergyPlus/DataHeatBalance.hh
@@ -272,6 +272,7 @@ namespace DataHeatBalance {
AirTerminalUserDefined,
PackagedTESCoilTank,
ElectricEquipmentITEAirCooled,
+ ElectricEquipmentITELiquidCooled,
SecCoolingDXCoilSingleSpeed,
SecHeatingDXCoilSingleSpeed,
SecCoolingDXCoilTwoSpeed,
@@ -355,6 +356,7 @@ namespace DataHeatBalance {
"AIRTERMINAL:SINGLEDUCT:USERDEFINED",
"COIL:COOLING:DX:SINGLESPEED:THERMALSTORAGE",
"ELECTRICEQUIPMENT:ITE:AIRCOOLED",
+ "ELECTRICEQUIPMENT:ITE:LIQUIDCOOLED",
"COIL:COOLING:DX:SINGLESPEED",
"COIL:HEATING:DX:SINGLESPEED",
"COIL:COOLING:DX:TWOSPEED",
@@ -413,6 +415,7 @@ namespace DataHeatBalance {
"AirTerminal:SingleDuct:UserDefined",
"Coil:Cooling:DX:SingleSpeed:ThermalStorage",
"ElectricEquipment:ITE:AirCooled",
+ "ElectricEquipment:ITE:LiquidCooled",
"Coil:Cooling:DX:SingleSpeed",
"Coil:Heating:DX:SingleSpeed",
"Coil:Cooling:DX:TwoSpeed",
@@ -987,6 +990,29 @@ namespace DataHeatBalance {
Num
};
+ // Liquid-cooled ITE Equipment cooling coil type (Cooling Coil N Object Type field)
+ enum class ITECoolingCoilType
+ {
+ Invalid = -1,
+ ColdPlate,
+ UserDefined,
+ Num
+ };
+ static constexpr std::array(ITECoolingCoilType::Num)> ITECoolingCoilTypeNamesUC = {
+ "COIL:COOLING:ITE:COLDPLATE", "COIL:COOLING:ITE:USERDEFINED"};
+
+ enum class LiquidITERptVars
+ {
+ Invalid = -1,
+ CPU = 0, // ITE CPU Electric Power/Energy
+ Fan, // ITE Fan Electric Power/Energy
+ TotalElectric, // ITE Total Electric Power/Energy
+ TotalHeatGen, // ITE Total Heat Generation Power(Rate)/Energy
+ LiquidHeatGain, // ITE Liquid Heat Gain Power(Rate)/Energy
+ AirHeatGain, // ITE Air Heat Gain to Zone Power(Rate)/Energy - convective gain
+ Num
+ };
+
struct ITEquipData // IT Equipment
{
// Members
@@ -1061,6 +1087,40 @@ namespace DataHeatBalance {
Real64 RHBelowDeltaRH = 0.0; // ITE Air Inlet Relative Humidity Difference Below Operating Range [%]
};
+ // One Cooling Coil N Object Type/Name/Load Fraction slot for ElectricEquipment:ITE:LiquidCooled
+ struct ITELiquidCoolingCoilSpec
+ {
+ ITECoolingCoilType CoilType = ITECoolingCoilType::Invalid; // Cooling Coil N Object Type
+ std::string CoilName; // Cooling Coil N Name
+ Real64 LoadFraction = 0.0; // Cooling Coil N Load Fraction (static)
+ Sched::Schedule *LoadFractionSched = nullptr; // Cooling Coil N Load Fraction Schedule Name
+ };
+
+ static constexpr int MaxITELiquidCoolingCoils = 4;
+
+ struct ITEquipDataLiquidCooled // Liquid-Cooled IT Equipment
+ {
+ // Members
+ std::string Name; // EQUIPMENT object name
+ int ZonePtr = 0; // Which zone internal gain is in
+ int spaceIndex = 0; // Space index for this equipment instance
+ Sched::Schedule *availSched = nullptr; // Availability schedule
+ Sched::Schedule *computeLoadSched = nullptr; // Compute Load Schedule (0.0-1.0 CPU loading fraction)
+ Real64 DesignTotalPower = 0.0; // Design Power Input [W]
+ Real64 Multiplier = 1.0; // Scales power and heat to represent multiple identical racks
+ Real64 DesignFanPowerInputFraction = 0.0; // Fraction of design power input that is for auxiliary fans
+ int PowerModifierCurve = 0; // Index for power modifier function of CPU loading (x) and zone air temperature (y) curve
+ Real64 LiquidHeatCaptureFractionDesign = 0.8; // Design (nominal) fraction of total heat generation captured by the liquid loop
+ Sched::Schedule *liquidHeatCaptureFracSched = nullptr; // Optional schedule multiplying the design liquid heat capture fraction
+
+ std::array CoolingCoils;
+
+ // Report variables
+ std::array PowerRpt;
+ std::array EnergyRpt;
+ Real64 LiquidHeatCaptureFraction = 0.0; // Current effective liquid heat capture fraction []
+ };
+
struct BBHeatData
{
// Members
@@ -1736,6 +1796,10 @@ namespace DataHeatBalance {
Real64 ITEqTimeAboveRH = 0.0; // Zone ITE Air Inlet Relative Humidity Above Operating Range Time [hr]
Real64 ITEqTimeBelowRH = 0.0; // Zone ITE Air Inlet Relative Humidity Below Operating Range Time [hr]
Real64 ITEAdjReturnTemp = 0.0; // Zone ITE Adjusted Return Air Temperature
+ // Liquid-Cooled IT Equipment
+ std::array LiquidITEPowerRpt;
+ std::array LiquidITEEnergyRpt;
+ Real64 LiquidITEHeatCaptureFraction = 0.0; // Zone ITE Liquid Heat Capture Fraction []
// Overall Zone Variables
Real64 TotRadiantGain = 0.0;
Real64 TotVisHeatGain = 0.0;
@@ -1856,6 +1920,7 @@ struct HeatBalanceData : BaseGlobalStruct
int TotHWEquip = 0; // Total Hot Water Equipment instances after expansion to spaces
int TotStmEquip = 0; // Total Steam Equipment instances after expansion to spaces
int TotITEquip = 0; // Total IT Equipment instances after expansion to spaces
+ int TotITELiquidCooledEquip = 0; // Total Liquid-Cooled IT Equipment instances after expansion to spaces
int TotInfiltration = 0; // Total Infiltration (all types) instances after expansion to spaces
int TotVentilation = 0; // Total Ventilation (all types) instances after expansion to spaces
int TotMixing = 0; // Total Mixing Statementsn instances after expansion to spaces
@@ -2017,6 +2082,7 @@ struct HeatBalanceData : BaseGlobalStruct
EPVector ZoneHWEq;
EPVector ZoneSteamEq;
EPVector ZoneITEq;
+ EPVector ZoneITELiquidCooled;
EPVector ZoneBBHeat;
EPVector Infiltration;
EPVector Ventilation;
diff --git a/src/EnergyPlus/DisplacementVentMgr.hh b/src/EnergyPlus/DisplacementVentMgr.hh
index 00710412cf1..27ecbcaf0b6 100644
--- a/src/EnergyPlus/DisplacementVentMgr.hh
+++ b/src/EnergyPlus/DisplacementVentMgr.hh
@@ -78,7 +78,7 @@ namespace RoomAir {
void CalcDispVent3Node(EnergyPlusData &state, int ZoneNum); // Which Zonenum
- static constexpr std::array IntGainTypesOccupied = {
+ static constexpr std::array IntGainTypesOccupied = {
DataHeatBalance::IntGainType::People,
DataHeatBalance::IntGainType::WaterHeaterMixed,
DataHeatBalance::IntGainType::WaterHeaterStratified,
@@ -87,6 +87,7 @@ namespace RoomAir {
DataHeatBalance::IntGainType::ThermalStorageHotWaterStratified,
DataHeatBalance::IntGainType::ElectricEquipment,
DataHeatBalance::IntGainType::ElectricEquipmentITEAirCooled,
+ DataHeatBalance::IntGainType::ElectricEquipmentITELiquidCooled,
DataHeatBalance::IntGainType::GasEquipment,
DataHeatBalance::IntGainType::HotWaterEquipment,
DataHeatBalance::IntGainType::SteamEquipment,
diff --git a/src/EnergyPlus/InternalHeatGains.cc b/src/EnergyPlus/InternalHeatGains.cc
index b732480364a..facfb71685e 100644
--- a/src/EnergyPlus/InternalHeatGains.cc
+++ b/src/EnergyPlus/InternalHeatGains.cc
@@ -133,8 +133,9 @@ namespace InternalHeatGains {
static constexpr std::array IntGainTypesPeople = {DataHeatBalance::IntGainType::People};
static constexpr std::array IntGainTypesLight = {DataHeatBalance::IntGainType::Lights};
- static constexpr std::array IntGainTypesEquip = {DataHeatBalance::IntGainType::ElectricEquipment,
+ static constexpr std::array IntGainTypesEquip = {DataHeatBalance::IntGainType::ElectricEquipment,
DataHeatBalance::IntGainType::ElectricEquipmentITEAirCooled,
+ DataHeatBalance::IntGainType::ElectricEquipmentITELiquidCooled,
DataHeatBalance::IntGainType::GasEquipment,
DataHeatBalance::IntGainType::HotWaterEquipment,
DataHeatBalance::IntGainType::SteamEquipment,
@@ -314,6 +315,7 @@ namespace InternalHeatGains {
const std::string stmEqModuleObject = "SteamEquipment";
const std::string othEqModuleObject = "OtherEquipment";
const std::string itEqModuleObject = "ElectricEquipment:ITE:AirCooled";
+ const std::string itEqLiquidCooledModuleObject = "ElectricEquipment:ITE:LiquidCooled";
const std::string bbModuleObject = "ZoneBaseboard:OutdoorTemperatureControlled";
const std::string contamSSModuleObject = "ZoneContaminantSourceAndSink:CarbonDioxide";
@@ -340,6 +342,7 @@ namespace InternalHeatGains {
stmEqModuleObject,
othEqModuleObject,
itEqModuleObject,
+ itEqLiquidCooledModuleObject,
bbModuleObject,
contamSSModuleObject}) {
state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, moduleName, NumParams, IHGNumAlphas, IHGNumNumbers);
@@ -2419,6 +2422,162 @@ namespace InternalHeatGains {
}
} // TotITEquip > 0
+ // ElectricEquipment:ITE:LiquidCooled
+ EPVector iTEqLiquidCooledObjects;
+ int numZoneITEqLiquidCooledStatements = 0;
+ // Note that this object type does not support ZoneList (matches ElectricEquipment:ITE:AirCooled)
+ bool liquidCooledZoneListNotAllowed = true;
+ setupIHGZonesAndSpaces(state,
+ itEqLiquidCooledModuleObject,
+ iTEqLiquidCooledObjects,
+ numZoneITEqLiquidCooledStatements,
+ state.dataHeatBal->TotITELiquidCooledEquip,
+ ErrorsFound,
+ liquidCooledZoneListNotAllowed);
+
+ if (state.dataHeatBal->TotITELiquidCooledEquip > 0) {
+ state.dataHeatBal->ZoneITELiquidCooled.allocate(state.dataHeatBal->TotITELiquidCooledEquip);
+ static constexpr std::array coilTypeAlphaIdx = {7, 10, 13, 16};
+ static constexpr std::array coilNameAlphaIdx = {8, 11, 14, 17};
+ static constexpr std::array coilLoadFracNumIdx = {5, 6, 7, 8};
+ static constexpr std::array coilLoadFracSchedAlphaIdx = {9, 12, 15, 18};
+
+ int itEqLCNum = 0;
+ for (int itEqLCInputNum = 1; itEqLCInputNum <= numZoneITEqLiquidCooledStatements; ++itEqLCInputNum) {
+
+ state.dataInputProcessing->inputProcessor->getObjectItem(state,
+ itEqLiquidCooledModuleObject,
+ itEqLCInputNum,
+ IHGAlphas,
+ IHGNumAlphas,
+ IHGNumbers,
+ IHGNumNumbers,
+ IOStat,
+ IHGNumericFieldBlanks,
+ IHGAlphaFieldBlanks,
+ IHGAlphaFieldNames,
+ IHGNumericFieldNames);
+
+ ErrorObjectHeader eoh{routineName, itEqLiquidCooledModuleObject, IHGAlphas(1)};
+
+ Sched::Schedule *availSchedPtr = nullptr;
+ if (IHGAlphaFieldBlanks(3)) {
+ availSchedPtr = Sched::GetScheduleAlwaysOn(state);
+ } else if ((availSchedPtr = Sched::GetSchedule(state, IHGAlphas(3))) == nullptr) {
+ ShowSevereItemNotFound(state, eoh, IHGAlphaFieldNames(3), IHGAlphas(3));
+ ErrorsFound = true;
+ }
+
+ Sched::Schedule *computeLoadSchedPtr = Sched::GetSchedule(state, IHGAlphas(4));
+ if (computeLoadSchedPtr == nullptr) {
+ ShowSevereItemNotFound(state, eoh, IHGAlphaFieldNames(4), IHGAlphas(4));
+ ErrorsFound = true;
+ } else if (!computeLoadSchedPtr->checkMinVal(state, Clusive::In, 0.0)) {
+ Sched::ShowSevereBadMin(state, eoh, IHGAlphaFieldNames(4), IHGAlphas(4), Clusive::In, 0.0);
+ ErrorsFound = true;
+ }
+
+ int powerModifierCurve = 0;
+ if (!IHGAlphaFieldBlanks(5)) {
+ powerModifierCurve = GetCurveIndex(state, IHGAlphas(5));
+ if (powerModifierCurve == 0) {
+ ShowSevereItemNotFound(state, eoh, IHGAlphaFieldNames(5), IHGAlphas(5));
+ ErrorsFound = true;
+ }
+ }
+
+ Sched::Schedule *liquidHeatCaptureFracSchedPtr = nullptr;
+ if (!IHGAlphaFieldBlanks(6)) {
+ liquidHeatCaptureFracSchedPtr = Sched::GetSchedule(state, IHGAlphas(6));
+ if (liquidHeatCaptureFracSchedPtr == nullptr) {
+ ShowSevereItemNotFound(state, eoh, IHGAlphaFieldNames(6), IHGAlphas(6));
+ ErrorsFound = true;
+ } else if (!liquidHeatCaptureFracSchedPtr->checkMinVal(state, Clusive::In, 0.0)) {
+ Sched::ShowSevereBadMin(state, eoh, IHGAlphaFieldNames(6), IHGAlphas(6), Clusive::In, 0.0);
+ ErrorsFound = true;
+ }
+ }
+
+ std::array coolingCoils;
+ for (int coilIdx = 0; coilIdx < DataHeatBalance::MaxITELiquidCoolingCoils; ++coilIdx) {
+ int const typeAlpha = coilTypeAlphaIdx[coilIdx];
+ int const nameAlpha = coilNameAlphaIdx[coilIdx];
+ int const loadFracNum = coilLoadFracNumIdx[coilIdx];
+ int const loadFracSchedAlpha = coilLoadFracSchedAlphaIdx[coilIdx];
+ auto &thisCoil = coolingCoils[coilIdx];
+
+ if (IHGAlphaFieldBlanks(typeAlpha) && IHGAlphaFieldBlanks(nameAlpha)) {
+ if (coilIdx == 0) {
+ ShowSevereEmptyField(state, eoh, IHGAlphaFieldNames(typeAlpha));
+ ErrorsFound = true;
+ }
+ continue; // this coil slot is not used
+ }
+
+ thisCoil.CoilType =
+ static_cast(getEnumValue(ITECoolingCoilTypeNamesUC, Util::makeUPPER(IHGAlphas(typeAlpha))));
+ if (thisCoil.CoilType == ITECoolingCoilType::Invalid) {
+ ShowSevereItemNotFound(state, eoh, IHGAlphaFieldNames(typeAlpha), IHGAlphas(typeAlpha));
+ ErrorsFound = true;
+ }
+ if (IHGAlphaFieldBlanks(nameAlpha)) {
+ ShowSevereEmptyField(state, eoh, IHGAlphaFieldNames(nameAlpha));
+ ErrorsFound = true;
+ } else {
+ thisCoil.CoilName = IHGAlphas(nameAlpha);
+ }
+
+ bool const hasLoadFrac = !IHGNumericFieldBlanks(loadFracNum);
+ bool const hasLoadFracSched = !IHGAlphaFieldBlanks(loadFracSchedAlpha);
+ if (hasLoadFrac && hasLoadFracSched) {
+ ShowWarningCustom(state,
+ eoh,
+ std::format("Both {} and {} are specified; the schedule will be used.",
+ IHGNumericFieldNames(loadFracNum),
+ IHGAlphaFieldNames(loadFracSchedAlpha)));
+ }
+ if (hasLoadFracSched) {
+ thisCoil.LoadFractionSched = Sched::GetSchedule(state, IHGAlphas(loadFracSchedAlpha));
+ if (thisCoil.LoadFractionSched == nullptr) {
+ ShowSevereItemNotFound(state, eoh, IHGAlphaFieldNames(loadFracSchedAlpha), IHGAlphas(loadFracSchedAlpha));
+ ErrorsFound = true;
+ }
+ } else if (hasLoadFrac) {
+ thisCoil.LoadFraction = IHGNumbers(loadFracNum);
+ }
+ }
+
+ auto &thisITEqLCInput = iTEqLiquidCooledObjects(itEqLCInputNum);
+ for (int Item1 = 1; Item1 <= thisITEqLCInput.numOfSpaces; ++Item1) {
+ ++itEqLCNum;
+ auto &thisZoneITELC = state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum);
+ int const spaceNum = thisITEqLCInput.spaceNums(Item1);
+ int const zoneNum = state.dataHeatBal->space(spaceNum).zoneNum;
+ thisZoneITELC.Name = thisITEqLCInput.names(Item1);
+ thisZoneITELC.spaceIndex = spaceNum;
+ thisZoneITELC.ZonePtr = zoneNum;
+ thisZoneITELC.availSched = availSchedPtr;
+ thisZoneITELC.computeLoadSched = computeLoadSchedPtr;
+ thisZoneITELC.DesignTotalPower = IHGNumbers(1);
+ thisZoneITELC.Multiplier = IHGNumericFieldBlanks(2) ? 1.0 : IHGNumbers(2);
+ thisZoneITELC.DesignFanPowerInputFraction = IHGNumericFieldBlanks(3) ? 0.0 : IHGNumbers(3);
+ thisZoneITELC.PowerModifierCurve = powerModifierCurve;
+ thisZoneITELC.LiquidHeatCaptureFractionDesign = IHGNumericFieldBlanks(4) ? 0.8 : IHGNumbers(4);
+ thisZoneITELC.liquidHeatCaptureFracSched = liquidHeatCaptureFracSchedPtr;
+ thisZoneITELC.CoolingCoils = coolingCoils;
+
+ if (!ErrorsFound) {
+ SetupSpaceInternalGain(state,
+ thisZoneITELC.spaceIndex,
+ 1.0,
+ thisZoneITELC.Name,
+ DataHeatBalance::IntGainType::ElectricEquipmentITELiquidCooled,
+ &thisZoneITELC.PowerRpt[(int)LiquidITERptVars::AirHeatGain]);
+ }
+ } // for itEqLCInputNum.NumOfSpaces
+ } // for itEqLCInputNum
+ } // TotITELiquidCooledEquip > 0
+
// ZoneBaseboard:OutdoorTemperatureControlled
EPVector zoneBBHeatObjects;
int numZoneBBHeatStatements = 0;
@@ -6228,6 +6387,180 @@ namespace InternalHeatGains {
addSpaceOutputs(spaceNum) = false;
}
+ // ElectricEquipment:ITE:LiquidCooled
+ // Object report variables
+ for (int itEqLCNum = 1; itEqLCNum <= state.dataHeatBal->TotITELiquidCooledEquip; ++itEqLCNum) {
+ // Set flags for zone and space total report variables
+ addZoneOutputs(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).ZonePtr) = true;
+ addSpaceOutputs(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).spaceIndex) = true;
+
+ constexpr std::array LiquidITEPowerOutputVariableStrings = {
+ "ITE CPU Electricity Rate",
+ "ITE Fan Electricity Rate",
+ "ITE Total Electricity Rate",
+ "ITE Total Heat Generation Rate",
+ "ITE Liquid Heat Gain Rate",
+ "ITE Air Heat Gain to Zone Rate"};
+ constexpr std::array LiquidITEEnergyOutputVariableStrings = {
+ "ITE CPU Electricity Energy",
+ "ITE Fan Electricity Energy",
+ "ITE Total Electricity Energy",
+ "ITE Total Heat Generation Energy",
+ "ITE Liquid Heat Gain Energy",
+ "ITE Air Heat Gain to Zone Energy"};
+
+ for (int i = 0; i < (int)LiquidITERptVars::Num; ++i) {
+ SetupOutputVariable(state,
+ LiquidITEPowerOutputVariableStrings[i],
+ Constant::Units::W,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).PowerRpt[i],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Average,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).Name);
+ }
+
+ SetupOutputVariable(state,
+ LiquidITEEnergyOutputVariableStrings[(int)LiquidITERptVars::CPU],
+ Constant::Units::J,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).EnergyRpt[(int)LiquidITERptVars::CPU],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Sum,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).Name,
+ Constant::eResource::Electricity,
+ OutputProcessor::Group::Building,
+ OutputProcessor::EndUseCat::InteriorEquipment,
+ "ITE-CPU",
+ state.dataHeatBal->Zone(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).ZonePtr).Name,
+ state.dataHeatBal->Zone(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).ZonePtr).Multiplier,
+ state.dataHeatBal->Zone(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).ZonePtr).ListMultiplier,
+ state.dataHeatBal->space(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).spaceIndex).spaceType);
+ SetupOutputVariable(state,
+ LiquidITEEnergyOutputVariableStrings[(int)LiquidITERptVars::Fan],
+ Constant::Units::J,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).EnergyRpt[(int)LiquidITERptVars::Fan],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Sum,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).Name,
+ Constant::eResource::Electricity,
+ OutputProcessor::Group::Building,
+ OutputProcessor::EndUseCat::InteriorEquipment,
+ "ITE-Fans",
+ state.dataHeatBal->Zone(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).ZonePtr).Name,
+ state.dataHeatBal->Zone(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).ZonePtr).Multiplier,
+ state.dataHeatBal->Zone(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).ZonePtr).ListMultiplier,
+ state.dataHeatBal->space(state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).spaceIndex).spaceType);
+ for (int i : {(int)LiquidITERptVars::TotalElectric,
+ (int)LiquidITERptVars::TotalHeatGen,
+ (int)LiquidITERptVars::LiquidHeatGain,
+ (int)LiquidITERptVars::AirHeatGain}) {
+ SetupOutputVariable(state,
+ LiquidITEEnergyOutputVariableStrings[i],
+ Constant::Units::J,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).EnergyRpt[i],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Sum,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).Name);
+ }
+
+ SetupOutputVariable(state,
+ "ITE Liquid Heat Capture Fraction",
+ Constant::Units::None,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).LiquidHeatCaptureFraction,
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Average,
+ state.dataHeatBal->ZoneITELiquidCooled(itEqLCNum).Name);
+ }
+
+ // Zone total report variables
+ for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
+ if (addZoneOutputs(zoneNum)) {
+ constexpr std::array ZoneLiquidITEPowerOutputVariableStrings = {
+ "Zone ITE CPU Electricity Rate",
+ "Zone ITE Fan Electricity Rate",
+ "Zone ITE Total Electricity Rate",
+ "Zone ITE Total Heat Generation Rate",
+ "Zone ITE Liquid Heat Gain Rate",
+ "Zone ITE Air Heat Gain to Zone Rate"};
+ constexpr std::array ZoneLiquidITEEnergyOutputVariableStrings = {
+ "Zone ITE CPU Electricity Energy",
+ "Zone ITE Fan Electricity Energy",
+ "Zone ITE Total Electricity Energy",
+ "Zone ITE Total Heat Generation Energy",
+ "Zone ITE Liquid Heat Gain Energy",
+ "Zone ITE Air Heat Gain to Zone Energy"};
+ for (int i = 0; i < (int)LiquidITERptVars::Num; ++i) {
+ SetupOutputVariable(state,
+ ZoneLiquidITEPowerOutputVariableStrings[i],
+ Constant::Units::W,
+ state.dataHeatBal->ZoneRpt(zoneNum).LiquidITEPowerRpt[i],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Average,
+ state.dataHeatBal->Zone(zoneNum).Name);
+ SetupOutputVariable(state,
+ ZoneLiquidITEEnergyOutputVariableStrings[i],
+ Constant::Units::J,
+ state.dataHeatBal->ZoneRpt(zoneNum).LiquidITEEnergyRpt[i],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Sum,
+ state.dataHeatBal->Zone(zoneNum).Name);
+ }
+ SetupOutputVariable(state,
+ "Zone ITE Liquid Heat Capture Fraction",
+ Constant::Units::None,
+ state.dataHeatBal->ZoneRpt(zoneNum).LiquidITEHeatCaptureFraction,
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Average,
+ state.dataHeatBal->Zone(zoneNum).Name);
+ }
+ // Reset zone output flag
+ addZoneOutputs(zoneNum) = false;
+ }
+
+ // Space total report variables
+ for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
+ if (addSpaceOutputs(spaceNum)) {
+ constexpr std::array SpaceLiquidITEPowerOutputVariableStrings = {
+ "Space ITE CPU Electricity Rate",
+ "Space ITE Fan Electricity Rate",
+ "Space ITE Total Electricity Rate",
+ "Space ITE Total Heat Generation Rate",
+ "Space ITE Liquid Heat Gain Rate",
+ "Space ITE Air Heat Gain to Zone Rate"};
+ constexpr std::array SpaceLiquidITEEnergyOutputVariableStrings = {
+ "Space ITE CPU Electricity Energy",
+ "Space ITE Fan Electricity Energy",
+ "Space ITE Total Electricity Energy",
+ "Space ITE Total Heat Generation Energy",
+ "Space ITE Liquid Heat Gain Energy",
+ "Space ITE Air Heat Gain to Zone Energy"};
+ for (int i = 0; i < (int)LiquidITERptVars::Num; ++i) {
+ SetupOutputVariable(state,
+ SpaceLiquidITEPowerOutputVariableStrings[i],
+ Constant::Units::W,
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEPowerRpt[i],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Average,
+ state.dataHeatBal->space(spaceNum).Name);
+ SetupOutputVariable(state,
+ SpaceLiquidITEEnergyOutputVariableStrings[i],
+ Constant::Units::J,
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEEnergyRpt[i],
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Sum,
+ state.dataHeatBal->space(spaceNum).Name);
+ }
+ SetupOutputVariable(state,
+ "Space ITE Liquid Heat Capture Fraction",
+ Constant::Units::None,
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEHeatCaptureFraction,
+ OutputProcessor::TimeStepType::Zone,
+ OutputProcessor::StoreType::Average,
+ state.dataHeatBal->space(spaceNum).Name);
+ }
+ // Reset space output flag
+ addSpaceOutputs(spaceNum) = false;
+ }
+
// Object report variables
for (int bbHeatNum = 1; bbHeatNum <= state.dataHeatBal->TotBBHeat; ++bbHeatNum) {
// Set flags for zone and space total report variables
@@ -6935,6 +7268,10 @@ namespace InternalHeatGains {
CalcZoneITEq(state);
}
+ if (state.dataHeatBal->TotITELiquidCooledEquip > 0) {
+ CalcZoneITEqLiquidCooled(state);
+ }
+
CalcWaterThermalTankZoneGains(state);
PipeHeatTransfer::PipeHTData::CalcZonePipesHeatGain(state);
CalcWaterUseZoneGains(state);
@@ -7646,6 +7983,98 @@ namespace InternalHeatGains {
} // End CalcZoneITEq
+ void CalcZoneITEqLiquidCooled(EnergyPlusData &state)
+ {
+
+ // PURPOSE OF THIS SUBROUTINE:
+ // This subroutine calculates the power consumption, total heat generation, and the split of that
+ // heat generation between the liquid cooling loop and the zone air for ElectricEquipment:ITE:LiquidCooled.
+ //
+ // Note: The cooling coils referenced by this object (Coil:Cooling:ITE:ColdPlate / Coil:Cooling:ITE:UserDefined)
+ // do not exist yet, so the liquid heat capture fraction is applied directly with no physical capacity limit
+ // imposed by the coil. Once those coil objects exist, Liquid Heat Gain should be capped at the coil's
+ // physical maximum, with any shortfall added to Air Heat Gain to Zone instead.
+
+ using Curve::CurveValue;
+
+ for (int Loop = 1; Loop <= state.dataHeatBal->TotITELiquidCooledEquip; ++Loop) {
+ for (int i = 0; i < (int)LiquidITERptVars::Num; ++i) {
+ state.dataHeatBal->ZoneITELiquidCooled(Loop).PowerRpt[i] = 0.0;
+ state.dataHeatBal->ZoneITELiquidCooled(Loop).EnergyRpt[i] = 0.0;
+ }
+ state.dataHeatBal->ZoneITELiquidCooled(Loop).LiquidHeatCaptureFraction = 0.0;
+ }
+
+ for (int Loop = 1; Loop <= state.dataGlobal->NumOfZones; ++Loop) {
+ for (int i = 0; i < (int)LiquidITERptVars::Num; ++i) {
+ state.dataHeatBal->ZoneRpt(Loop).LiquidITEPowerRpt[i] = 0.0;
+ state.dataHeatBal->ZoneRpt(Loop).LiquidITEEnergyRpt[i] = 0.0;
+ }
+ state.dataHeatBal->ZoneRpt(Loop).LiquidITEHeatCaptureFraction = 0.0;
+ }
+
+ for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
+ for (int i = 0; i < (int)LiquidITERptVars::Num; ++i) {
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEPowerRpt[i] = 0.0;
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEEnergyRpt[i] = 0.0;
+ }
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEHeatCaptureFraction = 0.0;
+ }
+
+ for (int Loop = 1; Loop <= state.dataHeatBal->TotITELiquidCooledEquip; ++Loop) {
+ auto &thisEquip = state.dataHeatBal->ZoneITELiquidCooled(Loop);
+ int const NZ = thisEquip.ZonePtr;
+ int const spaceNum = thisEquip.spaceIndex;
+
+ bool const available = (thisEquip.availSched->getCurrentVal() > 0.0);
+ Real64 const computeLoadFrac = thisEquip.computeLoadSched->getCurrentVal();
+ Real64 const zoneMAT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(NZ).MAT;
+
+ Real64 const powerModifier =
+ (thisEquip.PowerModifierCurve != 0) ? CurveValue(state, thisEquip.PowerModifierCurve, computeLoadFrac, zoneMAT) : 1.0;
+
+ Real64 totalPower = 0.0;
+ if (available) {
+ totalPower = max(thisEquip.DesignTotalPower * thisEquip.Multiplier * computeLoadFrac * powerModifier, 0.0);
+ }
+ Real64 const fanPower = totalPower * thisEquip.DesignFanPowerInputFraction;
+ Real64 const cpuPower = totalPower - fanPower;
+ Real64 const totalHeatGen = totalPower; // all electricity input becomes heat
+
+ Real64 liquidHeatCaptureFraction = thisEquip.LiquidHeatCaptureFractionDesign;
+ if (thisEquip.liquidHeatCaptureFracSched != nullptr) {
+ liquidHeatCaptureFraction *= thisEquip.liquidHeatCaptureFracSched->getCurrentVal();
+ }
+ liquidHeatCaptureFraction = min(max(liquidHeatCaptureFraction, 0.0), 1.0);
+
+ // Cooling Coil 1-4 do not yet perform any physical capacity limiting (see function header note),
+ // so the full target liquid load is assumed to be captured by the liquid loop.
+ Real64 const liquidHeatGain = totalHeatGen * liquidHeatCaptureFraction;
+ Real64 const airHeatGain = totalHeatGen - liquidHeatGain;
+
+ thisEquip.PowerRpt[(int)LiquidITERptVars::CPU] = cpuPower;
+ thisEquip.PowerRpt[(int)LiquidITERptVars::Fan] = fanPower;
+ thisEquip.PowerRpt[(int)LiquidITERptVars::TotalElectric] = totalPower;
+ thisEquip.PowerRpt[(int)LiquidITERptVars::TotalHeatGen] = totalHeatGen;
+ thisEquip.PowerRpt[(int)LiquidITERptVars::LiquidHeatGain] = liquidHeatGain;
+ thisEquip.PowerRpt[(int)LiquidITERptVars::AirHeatGain] = airHeatGain;
+ thisEquip.LiquidHeatCaptureFraction = liquidHeatCaptureFraction;
+
+ for (int i = 0; i < (int)LiquidITERptVars::Num; ++i) {
+ thisEquip.EnergyRpt[i] = thisEquip.PowerRpt[i] * state.dataGlobal->TimeStepZoneSec;
+ state.dataHeatBal->ZoneRpt(NZ).LiquidITEPowerRpt[i] += thisEquip.PowerRpt[i];
+ state.dataHeatBal->ZoneRpt(NZ).LiquidITEEnergyRpt[i] += thisEquip.EnergyRpt[i];
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEPowerRpt[i] += thisEquip.PowerRpt[i];
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEEnergyRpt[i] += thisEquip.EnergyRpt[i];
+ }
+ // Zone/space Liquid Heat Capture Fraction is reported as the last computed instance's value;
+ // combining fractions across multiple instances in the same zone/space is not physically meaningful.
+ state.dataHeatBal->ZoneRpt(NZ).LiquidITEHeatCaptureFraction = liquidHeatCaptureFraction;
+ state.dataHeatBal->spaceRpt(spaceNum).LiquidITEHeatCaptureFraction = liquidHeatCaptureFraction;
+ }
+
+ } // End CalcZoneITEqLiquidCooled
+
void ReportInternalHeatGains(EnergyPlusData &state)
{
@@ -7667,10 +8096,11 @@ namespace InternalHeatGains {
// OutputDataStructure.doc (EnergyPlus documentation)
// SUBROUTINE LOCAL VARIABLE DECLARATIONS:
- static constexpr std::array TradIntGainTypes = {DataHeatBalance::IntGainType::People,
+ static constexpr std::array TradIntGainTypes = {DataHeatBalance::IntGainType::People,
DataHeatBalance::IntGainType::Lights,
DataHeatBalance::IntGainType::ElectricEquipment,
DataHeatBalance::IntGainType::ElectricEquipmentITEAirCooled,
+ DataHeatBalance::IntGainType::ElectricEquipmentITELiquidCooled,
DataHeatBalance::IntGainType::GasEquipment,
DataHeatBalance::IntGainType::HotWaterEquipment,
DataHeatBalance::IntGainType::SteamEquipment,
diff --git a/src/EnergyPlus/InternalHeatGains.hh b/src/EnergyPlus/InternalHeatGains.hh
index b6283340d69..6810580be03 100644
--- a/src/EnergyPlus/InternalHeatGains.hh
+++ b/src/EnergyPlus/InternalHeatGains.hh
@@ -129,6 +129,8 @@ namespace InternalHeatGains {
void CalcZoneITEq(EnergyPlusData &state);
+ void CalcZoneITEqLiquidCooled(EnergyPlusData &state);
+
void ReportInternalHeatGains(EnergyPlusData &state);
Real64 GetDesignLightingLevelForZone(EnergyPlusData &state, int const WhichZone); // name of zone
diff --git a/src/EnergyPlus/UFADManager.cc b/src/EnergyPlus/UFADManager.cc
index 892e7844cc2..9822db2d3be 100644
--- a/src/EnergyPlus/UFADManager.cc
+++ b/src/EnergyPlus/UFADManager.cc
@@ -692,7 +692,7 @@ namespace RoomAir {
} // END FLOOR
}
- static constexpr std::array IntGainTypesOccupied = {
+ static constexpr std::array IntGainTypesOccupied = {
DataHeatBalance::IntGainType::People,
DataHeatBalance::IntGainType::WaterHeaterMixed,
DataHeatBalance::IntGainType::WaterHeaterStratified,
@@ -701,6 +701,7 @@ namespace RoomAir {
DataHeatBalance::IntGainType::ThermalStorageHotWaterStratified,
DataHeatBalance::IntGainType::ElectricEquipment,
DataHeatBalance::IntGainType::ElectricEquipmentITEAirCooled,
+ DataHeatBalance::IntGainType::ElectricEquipmentITELiquidCooled,
DataHeatBalance::IntGainType::GasEquipment,
DataHeatBalance::IntGainType::HotWaterEquipment,
DataHeatBalance::IntGainType::SteamEquipment,
diff --git a/testfiles/1ZoneDataCenterITELiquidCooled.idf b/testfiles/1ZoneDataCenterITELiquidCooled.idf
new file mode 100644
index 00000000000..f77c544ebbe
--- /dev/null
+++ b/testfiles/1ZoneDataCenterITELiquidCooled.idf
@@ -0,0 +1,337 @@
+!1ZoneDataCenterITELiquidCooled.idf
+! Basic file description: Minimal single-zone test of ElectricEquipment:ITE:LiquidCooled.
+!
+! Highlights: Exercises the new ElectricEquipment:ITE:LiquidCooled object: a
+! transient Compute Load Schedule drives CPU/Fan electricity and total
+! heat generation, which is split between the liquid cooling loop and
+! the zone air using the Liquid Heat Capture Fraction. No cooling coil
+! objects exist yet (Coil:Cooling:ITE:ColdPlate is still under
+! development), so the Cooling Coil 1 fields reference a coil name that
+! is never instantiated; the parent object does not simulate the coil.
+!
+! Simulation Location/Run: Denver Centennial Golden CO USA WMO=724666, 1 week January run period
+!
+! Location: Denver, CO
+!
+! Run Period (Weather File): 1 week in January
+!
+! Run Control: No sizing, weather file run period only
+!
+! Building: Fictional 1 zone building with resistive walls, same envelope as
+! 1ZoneUncontrolled3SurfaceZone.idf.
+!
+! Floor Area: 232.25 m2
+! Number of Stories: 1
+!
+! Internal gains description: ElectricEquipment:ITE:LiquidCooled
+!
+! Interzone Surfaces: None
+! Internal Mass: None
+! People: None
+! Lights: None
+! Windows: 0
+! Detached Shading: None
+! Daylight: None
+! Natural Ventilation: None
+! Solar Distribution: MinimalShadowing
+!
+! HVAC: NA
+!
+! Zonal Equipment: NA
+! Central Air Handling Equipment: No
+! Coils: None (Coil:Cooling:ITE:ColdPlate not yet implemented)
+! Pumps: None
+! Boilers: None
+! Chillers: None
+! Towers: None
+
+ Version,26.2;
+
+ Timestep,4;
+
+ Building,
+ Simple One Zone (ITE Liquid Cooled), !- Name
+ 0, !- North Axis {deg}
+ Suburbs, !- Terrain
+ 0.04, !- Loads Convergence Tolerance Value {W}
+ 0.004, !- Temperature Convergence Tolerance Value {deltaC}
+ MinimalShadowing, !- Solar Distribution
+ 30, !- Maximum Number of Warmup Days
+ 6; !- Minimum Number of Warmup Days
+
+ HeatBalanceAlgorithm,ConductionTransferFunction;
+
+ SurfaceConvectionAlgorithm:Inside,TARP;
+
+ SurfaceConvectionAlgorithm:Outside,DOE-2;
+
+ SimulationControl,
+ No, !- Do Zone Sizing Calculation
+ No, !- Do System Sizing Calculation
+ No, !- Do Plant Sizing Calculation
+ Yes, !- Run Simulation for Sizing Periods
+ Yes, !- Run Simulation for Weather File Run Periods
+ No, !- Do HVAC Sizing Simulation for Sizing Periods
+ 1; !- Maximum Number of HVAC Sizing Simulation Passes
+
+ RunPeriod,
+ Jan Week 1, !- Name
+ 1, !- Begin Month
+ 1, !- Begin Day of Month
+ , !- Begin Year
+ 1, !- End Month
+ 7, !- End Day of Month
+ , !- End Year
+ Tuesday, !- Day of Week for Start Day
+ Yes, !- Use Weather File Holidays and Special Days
+ Yes, !- Use Weather File Daylight Saving Period
+ No, !- Apply Weekend Holiday Rule
+ Yes, !- Use Weather File Rain Indicators
+ Yes; !- Use Weather File Snow Indicators
+
+ Site:Location,
+ Denver Centennial Golden N_CO_USA Design_Conditions, !- Name
+ 39.74, !- Latitude {deg}
+ -105.18, !- Longitude {deg}
+ -7.00, !- Time Zone {hr}
+ 1829.00; !- Elevation {m}
+
+ ! WMO=724666 Time Zone=NAM: (GMT-07:00) Mountain Time (US & Canada)
+ ! Data Source=ASHRAE 2009 Annual Design Conditions
+ ! Denver Centennial Golden N_CO_USA Annual Heating Design Conditions Wind Speed=3m/s Wind Dir=340
+ ! Coldest Month=DEC
+ ! Denver Centennial Golden N_CO_USA Annual Heating 99%, MaxDB=-15.5°C
+
+ SizingPeriod:DesignDay,
+ Denver Centennial Golden N Ann Htg 99% Condns DB, !- Name
+ 12, !- Month
+ 21, !- Day of Month
+ WinterDesignDay, !- Day Type
+ -15.5, !- Maximum Dry-Bulb Temperature {C}
+ 0.0, !- Daily Dry-Bulb Temperature Range {deltaC}
+ , !- Dry-Bulb Temperature Range Modifier Type
+ , !- Dry-Bulb Temperature Range Modifier Day Schedule Name
+ Wetbulb, !- Humidity Condition Type
+ -15.5, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C}
+ , !- Humidity Condition Day Schedule Name
+ , !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir}
+ , !- Enthalpy at Maximum Dry-Bulb {J/kg}
+ , !- Daily Wet-Bulb Temperature Range {deltaC}
+ 81198., !- Barometric Pressure {Pa}
+ 3, !- Wind Speed {m/s}
+ 340, !- Wind Direction {deg}
+ No, !- Rain Indicator
+ No, !- Snow Indicator
+ No, !- Daylight Saving Time Indicator
+ ASHRAEClearSky, !- Solar Model Indicator
+ , !- Beam Solar Day Schedule Name
+ , !- Diffuse Solar Day Schedule Name
+ , !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless}
+ , !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless}
+ 0.00; !- Sky Clearness
+
+ ! Denver Centennial Golden N Annual Cooling Design Conditions Wind Speed=4.9m/s Wind Dir=0
+ ! Hottest Month=JUL
+ ! Denver Centennial Golden N_CO_USA Annual Cooling (DB=>MWB) 1%, MaxDB=32°C MWB=15.5°C
+
+ SizingPeriod:DesignDay,
+ Denver Centennial Golden N Ann Clg 1% Condns DB=>MWB, !- Name
+ 7, !- Month
+ 21, !- Day of Month
+ SummerDesignDay, !- Day Type
+ 32, !- Maximum Dry-Bulb Temperature {C}
+ 15.2, !- Daily Dry-Bulb Temperature Range {deltaC}
+ , !- Dry-Bulb Temperature Range Modifier Type
+ , !- Dry-Bulb Temperature Range Modifier Day Schedule Name
+ Wetbulb, !- Humidity Condition Type
+ 15.5, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C}
+ , !- Humidity Condition Day Schedule Name
+ , !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir}
+ , !- Enthalpy at Maximum Dry-Bulb {J/kg}
+ , !- Daily Wet-Bulb Temperature Range {deltaC}
+ 81198., !- Barometric Pressure {Pa}
+ 4.9, !- Wind Speed {m/s}
+ 0, !- Wind Direction {deg}
+ No, !- Rain Indicator
+ No, !- Snow Indicator
+ No, !- Daylight Saving Time Indicator
+ ASHRAEClearSky, !- Solar Model Indicator
+ , !- Beam Solar Day Schedule Name
+ , !- Diffuse Solar Day Schedule Name
+ , !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless}
+ , !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless}
+ 1.00; !- Sky Clearness
+
+ Material:NoMass,
+ R13LAYER, !- Name
+ Rough, !- Roughness
+ 2.290965, !- Thermal Resistance {m2-K/W}
+ 0.9000000, !- Thermal Absorptance
+ 0.7500000, !- Solar Absorptance
+ 0.7500000; !- Visible Absorptance
+
+ Material:NoMass,
+ R31LAYER, !- Name
+ Rough, !- Roughness
+ 5.456, !- Thermal Resistance {m2-K/W}
+ 0.9000000, !- Thermal Absorptance
+ 0.7500000, !- Solar Absorptance
+ 0.7500000; !- Visible Absorptance
+
+ Material,
+ C5 - 4 IN HW CONCRETE, !- Name
+ MediumRough, !- Roughness
+ 0.1014984, !- Thickness {m}
+ 1.729577, !- Conductivity {W/m-K}
+ 2242.585, !- Density {kg/m3}
+ 836.8000, !- Specific Heat {J/kg-K}
+ 0.9000000, !- Thermal Absorptance
+ 0.6500000, !- Solar Absorptance
+ 0.6500000; !- Visible Absorptance
+
+ Construction,
+ R13WALL, !- Name
+ R13LAYER; !- Outside Layer
+
+ Construction,
+ FLOOR, !- Name
+ C5 - 4 IN HW CONCRETE; !- Outside Layer
+
+ Construction,
+ ROOF31, !- Name
+ R31LAYER; !- Outside Layer
+
+ Zone,
+ ZONE ONE, !- Name
+ 0, !- Direction of Relative North {deg}
+ 0, !- X Origin {m}
+ 0, !- Y Origin {m}
+ 0, !- Z Origin {m}
+ 1, !- Type
+ 1, !- Multiplier
+ autocalculate, !- Ceiling Height {m}
+ autocalculate; !- Volume {m3}
+
+ GlobalGeometryRules,
+ UpperLeftCorner, !- Starting Vertex Position
+ CounterClockWise, !- Vertex Entry Direction
+ World; !- Coordinate System
+
+ BuildingSurface:Detailed,
+ Zn001:Wall001, !- Name
+ Wall, !- Surface Type
+ R13WALL, !- Construction Name
+ ZONE ONE, !- Zone Name
+ , !- Space Name
+ Outdoors, !- Outside Boundary Condition
+ , !- Outside Boundary Condition Object
+ SunExposed, !- Sun Exposure
+ WindExposed, !- Wind Exposure
+ 0.5000000, !- View Factor to Ground
+ 4, !- Number of Vertices
+ 0,0,4.572000, !- X,Y,Z ==> Vertex 1 {m}
+ 0,0,0, !- X,Y,Z ==> Vertex 2 {m}
+ 60.96000,0,0, !- X,Y,Z ==> Vertex 3 {m}
+ 60.96000,0,4.572000; !- X,Y,Z ==> Vertex 4 {m}
+
+ BuildingSurface:Detailed,
+ Zn001:Flr001, !- Name
+ Floor, !- Surface Type
+ FLOOR, !- Construction Name
+ ZONE ONE, !- Zone Name
+ , !- Space Name
+ Adiabatic, !- Outside Boundary Condition
+ , !- Outside Boundary Condition Object
+ NoSun, !- Sun Exposure
+ NoWind, !- Wind Exposure
+ 1.000000, !- View Factor to Ground
+ 4, !- Number of Vertices
+ 15.24000,0.000000,0.0, !- X,Y,Z ==> Vertex 1 {m}
+ 0.000000,0.000000,0.0, !- X,Y,Z ==> Vertex 2 {m}
+ 0.000000,15.24000,0.0, !- X,Y,Z ==> Vertex 3 {m}
+ 15.24000,15.24000,0.0; !- X,Y,Z ==> Vertex 4 {m}
+
+ BuildingSurface:Detailed,
+ Zn001:Roof001, !- Name
+ Roof, !- Surface Type
+ ROOF31, !- Construction Name
+ ZONE ONE, !- Zone Name
+ , !- Space Name
+ Outdoors, !- Outside Boundary Condition
+ , !- Outside Boundary Condition Object
+ SunExposed, !- Sun Exposure
+ WindExposed, !- Wind Exposure
+ 0, !- View Factor to Ground
+ 4, !- Number of Vertices
+ 0.000000,15.24000,4.572, !- X,Y,Z ==> Vertex 1 {m}
+ 0.000000,0.000000,4.572, !- X,Y,Z ==> Vertex 2 {m}
+ 15.24000,0.000000,4.572, !- X,Y,Z ==> Vertex 3 {m}
+ 15.24000,15.24000,4.572; !- X,Y,Z ==> Vertex 4 {m}
+
+ ScheduleTypeLimits,
+ Fraction, !- Name
+ 0.0, !- Lower Limit Value
+ 1.0, !- Upper Limit Value
+ CONTINUOUS; !- Numeric Type
+
+ ScheduleTypeLimits,
+ On/Off, !- Name
+ 0, !- Lower Limit Value
+ 1, !- Upper Limit Value
+ DISCRETE; !- Numeric Type
+
+ Schedule:Constant,AlwaysOn,On/Off,1.0;
+
+ Schedule:Compact,
+ ComputeLoadSched, !- Name
+ Fraction, !- Schedule Type Limits Name
+ Through: 12/31, !- Field 1
+ For: AllDays, !- Field 2
+ Until: 12:00,0.5, !- Field 3
+ Until: 24:00,1.0; !- Field 5
+
+ ElectricEquipment:ITE:LiquidCooled,
+ ITE Rack 1, !- Name
+ ZONE ONE, !- Zone or Space Name
+ AlwaysOn, !- Availability Schedule Name
+ ComputeLoadSched, !- Compute Load Schedule Name
+ 10000, !- Design Power Input {W}
+ 2.0, !- Multiplier
+ 0.1, !- Design Fan Power Input Fraction
+ , !- IT Equipment Power Modifier Curve Name
+ 0.8, !- Liquid Heat Capture Fraction
+ , !- Liquid Heat Capture Fraction Schedule Name
+ Coil:Cooling:ITE:ColdPlate, !- Cooling Coil 1 Object Type
+ ColdPlate1, !- Cooling Coil 1 Name
+ 1.0, !- Cooling Coil 1 Load Fraction
+ ; !- Cooling Coil 1 Load Fraction Schedule Name
+
+ Output:Variable,*,Zone Mean Air Temperature,hourly;
+
+ Output:Variable,*,ITE CPU Electricity Rate,hourly;
+
+ Output:Variable,*,ITE Fan Electricity Rate,hourly;
+
+ Output:Variable,*,ITE Total Electricity Rate,hourly;
+
+ Output:Variable,*,ITE Total Heat Generation Rate,hourly;
+
+ Output:Variable,*,ITE Liquid Heat Capture Fraction,hourly;
+
+ Output:Variable,*,ITE Liquid Heat Gain Rate,hourly;
+
+ Output:Variable,*,ITE Air Heat Gain to Zone Rate,hourly;
+
+ Output:Variable,*,Zone ITE Total Electricity Rate,hourly;
+
+ Output:Variable,*,Zone ITE Air Heat Gain to Zone Rate,hourly;
+
+ Output:Variable,*,Space ITE Total Electricity Rate,hourly;
+
+ Output:VariableDictionary,IDF;
+
+ Output:Table:SummaryReports,
+ AllSummary; !- Report 1 Name
+
+ OutputControl:Table:Style,
+ HTML; !- Column Separator
diff --git a/testfiles/CMakeLists.txt b/testfiles/CMakeLists.txt
index 5fa84a2f784..b67400c6f7b 100644
--- a/testfiles/CMakeLists.txt
+++ b/testfiles/CMakeLists.txt
@@ -17,6 +17,7 @@ add_simulation_test(IDF_FILE VSHeatPumpWaterToAirWithRHControl.idf EPW_FILE USA_
add_simulation_test(IDF_FILE 1ZoneDataCenterCRAC_wPumpedDXCoolingCoil.idf EPW_FILE USA_CO_Golden-NREL.724666_TMY3.epw)
add_simulation_test(IDF_FILE 1ZoneDataCenterCRAC_wApproachTemp.idf EPW_FILE USA_CO_Golden-NREL.724666_TMY3.epw)
add_simulation_test(IDF_FILE 1ZoneDataCenterCRAC_wApproachTemp_ClassH1.idf EPW_FILE USA_CO_Golden-NREL.724666_TMY3.epw)
+add_simulation_test(IDF_FILE 1ZoneDataCenterITELiquidCooled.idf EPW_FILE USA_CO_Golden-NREL.724666_TMY3.epw)
add_simulation_test(IDF_FILE 2ZoneDataCenterHVAC_wEconomizer.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw)
add_simulation_test(IDF_FILE 2ZoneDataCenterHVAC_wEconomizer_ClassA1A2.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw)
add_simulation_test(IDF_FILE 1ZoneEvapCooler.idf EPW_FILE USA_CO_Golden-NREL.724666_TMY3.epw)
diff --git a/tst/EnergyPlus/unit/InternalHeatGains.unit.cc b/tst/EnergyPlus/unit/InternalHeatGains.unit.cc
index a66b5b17b60..b2de8ece5cc 100644
--- a/tst/EnergyPlus/unit/InternalHeatGains.unit.cc
+++ b/tst/EnergyPlus/unit/InternalHeatGains.unit.cc
@@ -3696,6 +3696,362 @@ TEST_F(EnergyPlusFixture, ITE_Env_Class_Update_Class_H1)
EXPECT_NE(thisspaceRpt.ITEqTimeBelowRH, state->dataGlobal->TimeStepZone);
EXPECT_EQ(thisspaceRpt.ITEqTimeOutOfOperRange, state->dataGlobal->TimeStepZone);
}
+
+TEST_F(EnergyPlusFixture, InternalHeatGains_ITELiquidCooled_BasicCalc)
+{
+ using namespace DataHeatBalance;
+
+ std::string const idf_objects = delimited_string({
+ " Zone,",
+ " ZONE ONE, !- Name",
+ " 0, !- Direction of Relative North {deg}",
+ " 0, !- X Origin {m}",
+ " 0, !- Y Origin {m}",
+ " 0, !- Z Origin {m}",
+ " 1, !- Type",
+ " 1, !- Multiplier",
+ " autocalculate, !- Ceiling Height {m}",
+ " autocalculate; !- Volume {m3}",
+
+ " Schedule:Constant,AlwaysOn,,1.0;",
+ " Schedule:Constant,ComputeLoadSched,,0.5;",
+
+ " ElectricEquipment:ITE:LiquidCooled,",
+ " ITE Rack 1, !- Name",
+ " ZONE ONE, !- Zone or Space Name",
+ " AlwaysOn, !- Availability Schedule Name",
+ " ComputeLoadSched, !- Compute Load Schedule Name",
+ " 10000, !- Design Power Input {W}",
+ " 2.0, !- Multiplier",
+ " 0.1, !- Design Fan Power Input Fraction",
+ " , !- IT Equipment Power Modifier Curve Name",
+ " 0.8, !- Liquid Heat Capture Fraction",
+ " , !- Liquid Heat Capture Fraction Schedule Name",
+ " Coil:Cooling:ITE:ColdPlate, !- Cooling Coil 1 Object Type",
+ " ColdPlate1, !- Cooling Coil 1 Name",
+ " 1.0, !- Cooling Coil 1 Load Fraction",
+ " ; !- Cooling Coil 1 Load Fraction Schedule Name",
+ });
+
+ ASSERT_TRUE(process_idf(idf_objects));
+ EXPECT_FALSE(has_err_output());
+
+ bool ErrorsFound(false);
+ state->dataGlobal->TimeStepsInHour = 1;
+ state->dataGlobal->MinutesInTimeStep = 60;
+ state->init_state(*state);
+
+ HeatBalanceManager::GetZoneData(*state, ErrorsFound);
+ ASSERT_FALSE(ErrorsFound);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance.allocate(1);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance(1).MAT = 24.0;
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance(1).airHumRat = 0.008;
+
+ InternalHeatGains::GetInternalHeatGainsInput(*state);
+ ASSERT_EQ(state->dataHeatBal->TotITELiquidCooledEquip, 1);
+
+ InternalHeatGains::CalcZoneITEqLiquidCooled(*state);
+ auto const &thisEquip = state->dataHeatBal->ZoneITELiquidCooled(1);
+
+ // Total Power = Design Power * Multiplier * computeLoadFrac (no curve) = 10000 * 2.0 * 0.5 = 10000 W
+ constexpr Real64 tol = 0.001;
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::TotalElectric], 10000.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::Fan], 1000.0, tol); // 10% of total
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::CPU], 9000.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::TotalHeatGen], 10000.0, tol); // all electricity is heat
+ EXPECT_NEAR(thisEquip.LiquidHeatCaptureFraction, 0.8, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::LiquidHeatGain], 8000.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::AirHeatGain], 2000.0, tol);
+
+ // Energy = Power * TimeStepZoneSec (1 hour timestep here)
+ EXPECT_NEAR(thisEquip.EnergyRpt[(int)LiquidITERptVars::TotalElectric], 10000.0 * state->dataGlobal->TimeStepZoneSec, tol);
+
+ // Zone-level rollup should match the single instance
+ auto const &thisZnRpt = state->dataHeatBal->ZoneRpt(1);
+ EXPECT_NEAR(thisZnRpt.LiquidITEPowerRpt[(int)LiquidITERptVars::TotalElectric], 10000.0, tol);
+ EXPECT_NEAR(thisZnRpt.LiquidITEPowerRpt[(int)LiquidITERptVars::AirHeatGain], 2000.0, tol);
+ EXPECT_NEAR(thisZnRpt.LiquidITEHeatCaptureFraction, 0.8, tol);
+}
+
+TEST_F(EnergyPlusFixture, InternalHeatGains_ITELiquidCooled_AvailabilityOff)
+{
+ using namespace DataHeatBalance;
+
+ std::string const idf_objects = delimited_string({
+ " Zone,",
+ " ZONE ONE, !- Name",
+ " 0, !- Direction of Relative North {deg}",
+ " 0, !- X Origin {m}",
+ " 0, !- Y Origin {m}",
+ " 0, !- Z Origin {m}",
+ " 1, !- Type",
+ " 1, !- Multiplier",
+ " autocalculate, !- Ceiling Height {m}",
+ " autocalculate; !- Volume {m3}",
+
+ " Schedule:Constant,AlwaysOff,,0.0;",
+ " Schedule:Constant,ComputeLoadSched,,1.0;",
+
+ " ElectricEquipment:ITE:LiquidCooled,",
+ " ITE Rack 1, !- Name",
+ " ZONE ONE, !- Zone or Space Name",
+ " AlwaysOff, !- Availability Schedule Name",
+ " ComputeLoadSched, !- Compute Load Schedule Name",
+ " 10000, !- Design Power Input {W}",
+ " 1.0, !- Multiplier",
+ " 0.1, !- Design Fan Power Input Fraction",
+ " , !- IT Equipment Power Modifier Curve Name",
+ " 0.8, !- Liquid Heat Capture Fraction",
+ " , !- Liquid Heat Capture Fraction Schedule Name",
+ " Coil:Cooling:ITE:ColdPlate, !- Cooling Coil 1 Object Type",
+ " ColdPlate1, !- Cooling Coil 1 Name",
+ " 1.0, !- Cooling Coil 1 Load Fraction",
+ " ; !- Cooling Coil 1 Load Fraction Schedule Name",
+ });
+
+ ASSERT_TRUE(process_idf(idf_objects));
+
+ bool ErrorsFound(false);
+ state->dataGlobal->TimeStepsInHour = 1;
+ state->dataGlobal->MinutesInTimeStep = 60;
+ state->init_state(*state);
+
+ HeatBalanceManager::GetZoneData(*state, ErrorsFound);
+ ASSERT_FALSE(ErrorsFound);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance.allocate(1);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance(1).MAT = 24.0;
+
+ InternalHeatGains::GetInternalHeatGainsInput(*state);
+ InternalHeatGains::CalcZoneITEqLiquidCooled(*state);
+ auto const &thisEquip = state->dataHeatBal->ZoneITELiquidCooled(1);
+
+ constexpr Real64 tol = 0.001;
+ // Equipment unavailable: all power/heat quantities are zero...
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::TotalElectric], 0.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::CPU], 0.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::Fan], 0.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::LiquidHeatGain], 0.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::AirHeatGain], 0.0, tol);
+ // ...but the reported Liquid Heat Capture Fraction reflects the design value regardless of availability.
+ EXPECT_NEAR(thisEquip.LiquidHeatCaptureFraction, 0.8, tol);
+}
+
+TEST_F(EnergyPlusFixture, InternalHeatGains_ITELiquidCooled_CaptureFractionScheduleAndClamp)
+{
+ using namespace DataHeatBalance;
+
+ std::string const idf_objects = delimited_string({
+ " Zone,",
+ " ZONE ONE, !- Name",
+ " 0, !- Direction of Relative North {deg}",
+ " 0, !- X Origin {m}",
+ " 0, !- Y Origin {m}",
+ " 0, !- Z Origin {m}",
+ " 1, !- Type",
+ " 1, !- Multiplier",
+ " autocalculate, !- Ceiling Height {m}",
+ " autocalculate; !- Volume {m3}",
+
+ " Schedule:Constant,AlwaysOn,,1.0;",
+ " Schedule:Constant,ComputeLoadSched,,1.0;",
+ " Schedule:Constant,CaptureFracMultSched,,1.5;", // > 1.0 to test clamping downstream
+
+ " ElectricEquipment:ITE:LiquidCooled,",
+ " ITE Rack 1, !- Name",
+ " ZONE ONE, !- Zone or Space Name",
+ " AlwaysOn, !- Availability Schedule Name",
+ " ComputeLoadSched, !- Compute Load Schedule Name",
+ " 10000, !- Design Power Input {W}",
+ " 1.0, !- Multiplier",
+ " 0.0, !- Design Fan Power Input Fraction",
+ " , !- IT Equipment Power Modifier Curve Name",
+ " 0.8, !- Liquid Heat Capture Fraction",
+ " CaptureFracMultSched, !- Liquid Heat Capture Fraction Schedule Name",
+ " Coil:Cooling:ITE:ColdPlate, !- Cooling Coil 1 Object Type",
+ " ColdPlate1, !- Cooling Coil 1 Name",
+ " 1.0, !- Cooling Coil 1 Load Fraction",
+ " ; !- Cooling Coil 1 Load Fraction Schedule Name",
+ });
+
+ ASSERT_TRUE(process_idf(idf_objects));
+
+ bool ErrorsFound(false);
+ state->dataGlobal->TimeStepsInHour = 1;
+ state->dataGlobal->MinutesInTimeStep = 60;
+ state->init_state(*state);
+
+ HeatBalanceManager::GetZoneData(*state, ErrorsFound);
+ ASSERT_FALSE(ErrorsFound);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance.allocate(1);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance(1).MAT = 24.0;
+
+ InternalHeatGains::GetInternalHeatGainsInput(*state);
+ InternalHeatGains::CalcZoneITEqLiquidCooled(*state);
+ auto const &thisEquip = state->dataHeatBal->ZoneITELiquidCooled(1);
+
+ constexpr Real64 tol = 0.001;
+ // Design fraction (0.8) * schedule (1.5) = 1.2, clamped down to 1.0
+ EXPECT_NEAR(thisEquip.LiquidHeatCaptureFraction, 1.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::TotalHeatGen], 10000.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::LiquidHeatGain], 10000.0, tol);
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::AirHeatGain], 0.0, tol);
+}
+
+TEST_F(EnergyPlusFixture, InternalHeatGains_ITELiquidCooled_PowerModifierCurve)
+{
+ using namespace DataHeatBalance;
+
+ std::string const idf_objects = delimited_string({
+ " Zone,",
+ " ZONE ONE, !- Name",
+ " 0, !- Direction of Relative North {deg}",
+ " 0, !- X Origin {m}",
+ " 0, !- Y Origin {m}",
+ " 0, !- Z Origin {m}",
+ " 1, !- Type",
+ " 1, !- Multiplier",
+ " autocalculate, !- Ceiling Height {m}",
+ " autocalculate; !- Volume {m3}",
+
+ " Schedule:Constant,AlwaysOn,,1.0;",
+ " Schedule:Constant,ComputeLoadSched,,1.0;",
+
+ " ElectricEquipment:ITE:LiquidCooled,",
+ " ITE Rack 1, !- Name",
+ " ZONE ONE, !- Zone or Space Name",
+ " AlwaysOn, !- Availability Schedule Name",
+ " ComputeLoadSched, !- Compute Load Schedule Name",
+ " 10000, !- Design Power Input {W}",
+ " 1.0, !- Multiplier",
+ " 0.0, !- Design Fan Power Input Fraction",
+ " PowerModCurve, !- IT Equipment Power Modifier Curve Name",
+ " 0.8, !- Liquid Heat Capture Fraction",
+ " , !- Liquid Heat Capture Fraction Schedule Name",
+ " Coil:Cooling:ITE:ColdPlate, !- Cooling Coil 1 Object Type",
+ " ColdPlate1, !- Cooling Coil 1 Name",
+ " 1.0, !- Cooling Coil 1 Load Fraction",
+ " ; !- Cooling Coil 1 Load Fraction Schedule Name",
+
+ " Curve:Biquadratic,",
+ " PowerModCurve, !- Name",
+ " 0.5, !- Coefficient1 Constant",
+ " 0.0, !- Coefficient2 x",
+ " 0.0, !- Coefficient3 x**2",
+ " 0.0, !- Coefficient4 y",
+ " 0.0, !- Coefficient5 y**2",
+ " 0.0, !- Coefficient6 x*y",
+ " 0.0, !- Minimum Value of x",
+ " 1.5, !- Maximum Value of x",
+ " -10, !- Minimum Value of y",
+ " 99.0, !- Maximum Value of y",
+ " 0.0, !- Minimum Curve Output",
+ " 99.0, !- Maximum Curve Output",
+ " Dimensionless, !- Input Unit Type for X",
+ " Temperature, !- Input Unit Type for Y",
+ " Dimensionless; !- Output Unit Type",
+ });
+
+ ASSERT_TRUE(process_idf(idf_objects));
+
+ bool ErrorsFound(false);
+ state->dataGlobal->TimeStepsInHour = 1;
+ state->dataGlobal->MinutesInTimeStep = 60;
+ state->init_state(*state);
+
+ HeatBalanceManager::GetZoneData(*state, ErrorsFound);
+ ASSERT_FALSE(ErrorsFound);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance.allocate(1);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance(1).MAT = 24.0;
+
+ InternalHeatGains::GetInternalHeatGainsInput(*state);
+ InternalHeatGains::CalcZoneITEqLiquidCooled(*state);
+ auto const &thisEquip = state->dataHeatBal->ZoneITELiquidCooled(1);
+
+ // Curve is a flat 0.5 regardless of (loading, temperature), so
+ // Total Power = 10000 * 1.0 * 1.0 (computeLoadFrac) * 0.5 (curve) = 5000 W
+ constexpr Real64 tol = 0.001;
+ EXPECT_NEAR(thisEquip.PowerRpt[(int)LiquidITERptVars::TotalElectric], 5000.0, tol);
+}
+
+TEST_F(EnergyPlusFixture, InternalHeatGains_ITELiquidCooled_ZoneRollupMultipleInstances)
+{
+ using namespace DataHeatBalance;
+
+ std::string const idf_objects = delimited_string({
+ " Zone,",
+ " ZONE ONE, !- Name",
+ " 0, !- Direction of Relative North {deg}",
+ " 0, !- X Origin {m}",
+ " 0, !- Y Origin {m}",
+ " 0, !- Z Origin {m}",
+ " 1, !- Type",
+ " 1, !- Multiplier",
+ " autocalculate, !- Ceiling Height {m}",
+ " autocalculate; !- Volume {m3}",
+
+ " Schedule:Constant,AlwaysOn,,1.0;",
+ " Schedule:Constant,ComputeLoadSched,,1.0;",
+
+ " ElectricEquipment:ITE:LiquidCooled,",
+ " ITE Rack 1, !- Name",
+ " ZONE ONE, !- Zone or Space Name",
+ " AlwaysOn, !- Availability Schedule Name",
+ " ComputeLoadSched, !- Compute Load Schedule Name",
+ " 10000, !- Design Power Input {W}",
+ " 1.0, !- Multiplier",
+ " 0.0, !- Design Fan Power Input Fraction",
+ " , !- IT Equipment Power Modifier Curve Name",
+ " 0.8, !- Liquid Heat Capture Fraction",
+ " , !- Liquid Heat Capture Fraction Schedule Name",
+ " Coil:Cooling:ITE:ColdPlate, !- Cooling Coil 1 Object Type",
+ " ColdPlate1, !- Cooling Coil 1 Name",
+ " 1.0, !- Cooling Coil 1 Load Fraction",
+ " ; !- Cooling Coil 1 Load Fraction Schedule Name",
+
+ " ElectricEquipment:ITE:LiquidCooled,",
+ " ITE Rack 2, !- Name",
+ " ZONE ONE, !- Zone or Space Name",
+ " AlwaysOn, !- Availability Schedule Name",
+ " ComputeLoadSched, !- Compute Load Schedule Name",
+ " 5000, !- Design Power Input {W}",
+ " 1.0, !- Multiplier",
+ " 0.0, !- Design Fan Power Input Fraction",
+ " , !- IT Equipment Power Modifier Curve Name",
+ " 0.5, !- Liquid Heat Capture Fraction",
+ " , !- Liquid Heat Capture Fraction Schedule Name",
+ " Coil:Cooling:ITE:ColdPlate, !- Cooling Coil 1 Object Type",
+ " ColdPlate2, !- Cooling Coil 1 Name",
+ " 1.0, !- Cooling Coil 1 Load Fraction",
+ " ; !- Cooling Coil 1 Load Fraction Schedule Name",
+ });
+
+ ASSERT_TRUE(process_idf(idf_objects));
+
+ bool ErrorsFound(false);
+ state->dataGlobal->TimeStepsInHour = 1;
+ state->dataGlobal->MinutesInTimeStep = 60;
+ state->init_state(*state);
+
+ HeatBalanceManager::GetZoneData(*state, ErrorsFound);
+ ASSERT_FALSE(ErrorsFound);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance.allocate(1);
+ state->dataZoneTempPredictorCorrector->zoneHeatBalance(1).MAT = 24.0;
+
+ InternalHeatGains::GetInternalHeatGainsInput(*state);
+ ASSERT_EQ(state->dataHeatBal->TotITELiquidCooledEquip, 2);
+
+ InternalHeatGains::CalcZoneITEqLiquidCooled(*state);
+
+ // Rack 1: 10000 W total, 8000 W liquid, 2000 W air
+ // Rack 2: 5000 W total, 2500 W liquid, 2500 W air
+ // Zone total: 15000 W total, 10500 W liquid, 4500 W air
+ constexpr Real64 tol = 0.001;
+ auto const &thisZnRpt = state->dataHeatBal->ZoneRpt(1);
+ EXPECT_NEAR(thisZnRpt.LiquidITEPowerRpt[(int)LiquidITERptVars::TotalElectric], 15000.0, tol);
+ EXPECT_NEAR(thisZnRpt.LiquidITEPowerRpt[(int)LiquidITERptVars::LiquidHeatGain], 10500.0, tol);
+ EXPECT_NEAR(thisZnRpt.LiquidITEPowerRpt[(int)LiquidITERptVars::AirHeatGain], 4500.0, tol);
+}
+
TEST_F(EnergyPlusFixture, InternalHeatGains_SpaceAllocation)
{