import os
import numpy as np
[docs]
class CPU():
def __init__(self, full_load_pwr = None, idle_pwr = None, cpu_config = None):
"""CPU class in charge of the energy consumption and termal calculations of the individuals CPUs
in a Rack.
Args:
full_load_pwr (float, optional): Power at full capacity.
idle_pwr (float, optional): Power while idle.
cpu_config (config): Configuration for the DC.
"""
self.cpu_config = cpu_config
self.full_load_pwr = full_load_pwr if not None else self.cpu_config.HP_PROLIANT[0]
self.idle_pwr = idle_pwr if not None else self.cpu_config.HP_PROLIANT[1]
self.m_cpu = None
self.c_cpu = None
self.m_itfan = None
self.c_itfan = None
self.cpu_curve1()
self.itfan_curve2()
self.v_fan = None # needed later for calculating outlet temperature
self.itfan_v_ratio_at_inlet_temp = None
self.total_DC_full_load = None
[docs]
def cpu_curve1(self,):
"""
initialize the cpu power ratio curve at different IT workload ratios as a function of inlet temperatures [3]
"""
# curve parameters at lowest ITE utilization 0%
self.m_cpu = (self.cpu_config.CPU_POWER_RATIO_UB[0]-self.cpu_config.CPU_POWER_RATIO_LB[0])/(self.cpu_config.INLET_TEMP_RANGE[1]-self.cpu_config.INLET_TEMP_RANGE[0])
self.c_cpu = self.cpu_config.CPU_POWER_RATIO_UB[0] - self.m_cpu*self.cpu_config.INLET_TEMP_RANGE[1]
# max vertical shift in power ratio curve at a given point in inlet temperature for 100% change in ITE input load pct
self.ratio_shift_max_cpu = self.cpu_config.CPU_POWER_RATIO_LB[1] - self.cpu_config.CPU_POWER_RATIO_LB[0]
[docs]
def itfan_curve2(self,):
"""
initialize the itfan velocity ratio curve at different IT workload ratios as a function of inlet temperatures [3]
"""
# curve parameters at ITE utilization 25%
self.m_itfan = (self.cpu_config.IT_FAN_AIRFLOW_RATIO_UB[0]-self.cpu_config.IT_FAN_AIRFLOW_RATIO_LB[0])/(self.cpu_config.INLET_TEMP_RANGE[1]-self.cpu_config.INLET_TEMP_RANGE[0])
self.c_itfan = self.cpu_config.IT_FAN_AIRFLOW_RATIO_UB[0] - self.m_itfan*self.cpu_config.INLET_TEMP_RANGE[1]
# max vertical shift in fan flow ratio curve at a given point for 75% change in ITE input load pct
self.ratio_shift_max_itfan = self.cpu_config.IT_FAN_AIRFLOW_RATIO_LB[1] - self.cpu_config.IT_FAN_AIRFLOW_RATIO_LB[0]
[docs]
class Rack():
def __init__(self, CPU_config_list, max_W_per_rack = 10000, rack_config = None): # [3] Table 2 Mid-tier data center
"""Defines the rack as a collection of CPUs
Args:
CPU_config_list (config): CPU configuration
max_W_per_rack (int): Maximun power allowed for a whole rack. Defaults to 10000.
rack_config (config): Rack configuration. Defaults to None.
"""
self.rack_config = rack_config
self.CPU_list = []
self.current_rack_load = 0
for CPU_config in CPU_config_list:
self.CPU_list.append(CPU(full_load_pwr = CPU_config['full_load_pwr'], idle_pwr = CPU_config['idle_pwr'], cpu_config = self.rack_config))
self.current_rack_load += self.CPU_list[-1].full_load_pwr
if self.current_rack_load>= max_W_per_rack:
self.CPU_list.pop()
break
self.num_CPUs = len(self.CPU_list)
self.cpu_and_fan_init()
self.v_fan_rack = None # will be later pointing to a 1d np array whose shape is the number of cpus in the rack class
[docs]
def cpu_and_fan_init(self,):
#common to both cpu and fan
inlet_temp_lb,inlet_temp_ub =[],[]
# only for cpu
m_cpu,c_cpu,ratio_shift_max_cpu,\
idle_pwr, full_load_pwr = [],[],[],[],[]
# only for it fan
m_itfan,c_itfan,ratio_shift_max_itfan,\
ITFAN_REF_P,ITFAN_REF_V_RATIO,IT_FAN_FULL_LOAD_V = \
[],[],[],[],[],[]
self.m_coefficient = 10 #1 -> 10
self.c_coefficient = 5 #1 -> 5
self.it_slope = 20 #100 -> 20
for CPU_item in self.CPU_list:
#common to both cpu and fan
inlet_temp_lb.append(CPU_item.cpu_config.INLET_TEMP_RANGE[0])
inlet_temp_ub.append(CPU_item.cpu_config.INLET_TEMP_RANGE[1])
# only for cpu
m_cpu.append(CPU_item.m_cpu)
c_cpu.append(CPU_item.c_cpu)
ratio_shift_max_cpu.append(CPU_item.ratio_shift_max_cpu)
idle_pwr.append(CPU_item.idle_pwr)
full_load_pwr.append(CPU_item.full_load_pwr)
# only for itfan
m_itfan.append(CPU_item.m_itfan)
c_itfan.append(CPU_item.c_itfan)
ratio_shift_max_itfan.append(CPU_item.ratio_shift_max_itfan)
ITFAN_REF_P.append(CPU_item.cpu_config.ITFAN_REF_P)
ITFAN_REF_V_RATIO.append(CPU_item.cpu_config.ITFAN_REF_V_RATIO)
IT_FAN_FULL_LOAD_V.append(CPU_item.cpu_config.IT_FAN_FULL_LOAD_V)
# commont to both cpu and itfan
self.inlet_temp_lb,self.inlet_temp_ub =np.array(inlet_temp_lb),\
np.array(inlet_temp_ub)
# only for cpu
self.m_cpu,self.c_cpu,self.ratio_shift_max_cpu,\
self.idle_pwr, self.full_load_pwr = \
np.array(m_cpu),np.array(c_cpu),\
np.array(ratio_shift_max_cpu),\
np.array(idle_pwr), np.array(full_load_pwr)
# only for itfan
self.m_itfan,self.c_itfan,self.ratio_shift_max_itfan,\
self.ITFAN_REF_P,self.ITFAN_REF_V_RATIO,self.IT_FAN_FULL_LOAD_V = \
np.array(m_itfan),np.array(c_itfan),np.array(ratio_shift_max_itfan),\
np.array(ITFAN_REF_P),np.array(ITFAN_REF_V_RATIO),np.array(IT_FAN_FULL_LOAD_V)
[docs]
def compute_instantaneous_pwr(self,inlet_temp, ITE_load_pct):
"""Calculate the power consumption of the whole rack at the current step
Args:
inlet_temp (float): Room temperature
ITE_load_pct (float): Current CPU usage
Returns:
cpu_power (float): Current CPU power usage
"""
# Assuming that all CPUs have the same parameters
cpu = self.CPU_list[0]
tot_cpu_pwr = cpu.compute_instantaneous_cpu_pwr(inlet_temp, ITE_load_pct)*self.num_CPUs
tot_itfan_pwr = []
for CPU_item in self.CPU_list:
tot_itfan_pwr.append(CPU_item.compute_instantaneous_fan_pwr(inlet_temp, ITE_load_pct))
return tot_cpu_pwr, np.array(tot_itfan_pwr).sum()
[docs]
def compute_instantaneous_pwr_vecd(self, inlet_temp, ITE_load_pct):
# CPU
base_cpu_power_ratio = (self.m_cpu+0.05)*inlet_temp + self.c_cpu
cpu_power_ratio_at_inlet_temp = base_cpu_power_ratio + self.ratio_shift_max_cpu*(ITE_load_pct/100)
temp_arr = np.concatenate((self.idle_pwr.reshape(1,-1),
(self.full_load_pwr*cpu_power_ratio_at_inlet_temp).reshape(1,-1)),
axis=0)
cpu_power = np.max(temp_arr,axis=0)
# itfan
base_itfan_v_ratio = self.m_itfan*self.m_coefficient*inlet_temp + self.c_itfan*self.c_coefficient
itfan_v_ratio_at_inlet_temp = base_itfan_v_ratio + self.ratio_shift_max_itfan*(ITE_load_pct/self.it_slope)
itfan_pwr = self.ITFAN_REF_P * (itfan_v_ratio_at_inlet_temp/self.ITFAN_REF_V_RATIO) # [4] Eqn (3)
self.v_fan_rack = self.IT_FAN_FULL_LOAD_V*itfan_v_ratio_at_inlet_temp
return np.sum(cpu_power), np.sum(itfan_pwr)
[docs]
def get_average_rack_fan_v(self,):
"""Calculate the average fan velocity for each rack
Returns:
(float): Average fan flow rate for the rack
"""
return self.v_fan_rack[0]
[docs]
def get_total_rack_fan_v(self,):
"""Calculate the total fan velocity for each rack
Returns:
(float): Total fan flow rate for the rack
"""
return np.sum(self.v_fan_rack)
[docs]
def get_current_rack_load(self,):
"""Returns the total power consumption of the rack
Returns:
float: Total power consumption of the rack
"""
return self.current_rack_load
[docs]
def clamp_supply_approach_temp(self,supply_approach_temperature):
"""Returns the clamped delta/ supply approach temperature between the range [3.8, 5.3]
Returns:
float: Supply approach temperature
"""
return max(3.8, min(supply_approach_temperature, 5.3))
[docs]
class DataCenter_ITModel():
def __init__(self, num_racks, rack_supply_approach_temp_list, rack_CPU_config, max_W_per_rack = 10000, DC_ITModel_config = None, chiller_sizing = False) -> None:
"""Creates the DC from a giving DC configuration
Args:
num_racks (int): Number of racks in the DC
rack_supply_approach_temp_list (list[float]): models the supply approach temperature for each rack based on geometry and estimated from CFD
rack_CPU_config (list[list[dict]]): A list of lists where each list is associated with a rack.
It is a list of dictionaries with their full load and idle load values in W
chiller_sizing (bool): Whether to perform Chiller Power Sizing
"""
self.DC_ITModel_config = DC_ITModel_config
self.racks_list = []
self.rack_supply_approach_temp_list = rack_supply_approach_temp_list
self.rack_CPU_config = rack_CPU_config
self.rackwise_inlet_temp = []
for _, CPU_config_list in zip(range(num_racks),self.rack_CPU_config):
self.racks_list.append(Rack(CPU_config_list, max_W_per_rack = max_W_per_rack, rack_config=self.DC_ITModel_config))
self.total_datacenter_full_load()
self.tower_flow_rate = np.round(DC_ITModel_config.CT_WATER_FLOW_RATE * 3600, 4) # m³/hr
self.hot_water_temp = None # °C
self.cold_water_temp = None # °C
self.wet_bulb_temp = None # °C
self.cycles_of_concentration = 5
self.drift_rate = 0.01
[docs]
def compute_datacenter_IT_load_outlet_temp(self,ITE_load_pct_list, CRAC_setpoint):
"""Calculate the average outlet temperature of all the racks
Args:
ITE_load_pct_list (List[float]): CPU load for each rack
CRAC_setpoint (float): CRAC setpoint
Returns:
rackwise_cpu_pwr (List[float]): Rackwise CPU power usage
rackwise_itfan_pwr (List[float]): Rackwise IT fan power usage
rackwise_outlet_temp (List[float]): Rackwise outlet temperature
"""
rackwise_cpu_pwr = []
rackwise_itfan_pwr = []
rackwise_outlet_temp = []
rackwise_inlet_temp = []
c = 1.918
d = 1.096
e = 0.824
f = 0.526
g = -14.01
for rack, rack_supply_approach_temp, ITE_load_pct \
in zip(self.racks_list, self.rack_supply_approach_temp_list, ITE_load_pct_list):
#clamp supply approach temperatures
rack_supply_approach_temp = rack.clamp_supply_approach_temp(rack_supply_approach_temp)
rack_inlet_temp = rack_supply_approach_temp + CRAC_setpoint
rackwise_inlet_temp.append(rack_inlet_temp)
rack_cpu_power, rack_itfan_power = rack.compute_instantaneous_pwr_vecd(rack_inlet_temp, ITE_load_pct)
# Max IT Power : 83000
# Min IT Power : 28000
rackwise_cpu_pwr.append(rack_cpu_power)
rackwise_itfan_pwr.append(rack_itfan_power)
power_term = (rack_cpu_power + rack_itfan_power)**d
airflow_term = self.DC_ITModel_config.C_AIR*self.DC_ITModel_config.RHO_AIR*rack.get_total_rack_fan_v()**e * f
log_term = 0# h * np.log(max(power_term / airflow_term, 1)) # Log term, avoid log(0)
# rackwise_outlet_temp.append((rack_inlet_temp + (rack_cpu_power+rack_itfan_power)/(self.DC_ITModel_config.C_AIR*self.DC_ITModel_config.RHO_AIR*rack.get_total_rack_fan_v()*a) + b * (rack_cpu_power+rack_itfan_power) - c))
outlet_temp = rack_inlet_temp + c * power_term / airflow_term + g
if outlet_temp > 60:
print(f'WARNING, the outlet temperature is higher than 60C: {outlet_temp:.3f}')
if outlet_temp - rack_inlet_temp < 2:
print(f'There is something wrong with the delta calculation because is too small: {outlet_temp - rack_inlet_temp:.3f}')
print(f'Inlet Temp: {rack_inlet_temp:.3f}, Util: {ITE_load_pct}, ITE Power: {rack_cpu_power+rack_itfan_power:.3f}')
print(f'Power term: {power_term:.3f}, Airflow term: {airflow_term:.3f}, Log term: {log_term:.3f}')
print(f'Delta: {c * power_term / airflow_term + g + log_term:.3f}')
raise Exception("Sorry, no numbers below 2")
if ITE_load_pct > 95 and CRAC_setpoint < 16.5:
if outlet_temp - rack_inlet_temp < 2:
print(f'There is something wrong with the delta calculation for MAX is too small: {outlet_temp - rack_inlet_temp:.3f}')
print(f'Inlet Temp: {rack_inlet_temp:.3f}, ITE Power: {rack_cpu_power+rack_itfan_power:.3f}')
print(f'Power term: {power_term:.3f}, Airflow term: {airflow_term:.3f}, Log term: {log_term:.3f}')
print(f'Delta: {c * power_term / airflow_term + g + log_term:.3f}')
raise Exception("Sorry, no numbers below 2")
rackwise_outlet_temp.append(outlet_temp)
# rackwise_outlet_temp.append(
# rack_inlet_temp + \
# (rack_cpu_power+rack_itfan_power)/(self.DC_ITModel_config.C_AIR*self.DC_ITModel_config.RHO_AIR*rack.get_total_rack_fan_v())
# )
self.rackwise_inlet_temp = rackwise_inlet_temp
return rackwise_cpu_pwr, rackwise_itfan_pwr, rackwise_outlet_temp
[docs]
def total_datacenter_full_load(self,):
"""Calculate the total DC IT(IT CPU POWER + IT FAN POWER) power consumption
"""
x = [rack_item.get_current_rack_load() for rack_item in self.racks_list]
self.total_DC_full_load = sum(x)
[docs]
def calculate_cooling_tower_water_usage(self):
"""
Calculate the estimated water usage of the cooling tower.
This function uses the attributes set in the class to estimate the water usage based
[Sharma, R.K., Shah, A., Bash, C.E., Christian, T., & Patel, C.D. (2009). Water efficiency management in datacenters: Metrics and methodology. 2009 IEEE International Symposium on Sustainable Systems and Technology, 1-6.]
[Mohammed Shublaq, Ahmad K. Sleiti., (2020). Experimental analysis of water evaporation losses in cooling towers using filters]
https://spxcooling.com/water-calculator/
"""
# We're assuming m³/hr, which is standard
# Calculate the range (difference between hot and cold water temperature)
range_temp = self.hot_water_temp - self.cold_water_temp
y_intercept = 0.3528 * range_temp + 0.101
# The water usage estimation formula would need to be derived from the graph you provided.
norm_water_usage = 0.044 * self.wet_bulb_temp + y_intercept
water_usage = np.clip(norm_water_usage, 0, None)
water_usage += water_usage * self.drift_rate # adjust for drift
# Convert m³/hr to the desired unit (e.g., liters per 15 minutes) if necessary
# There are 1000 liters in a cubic meter. There are 4 15-minute intervals in an hour.
water_usage_liters_per_15min = np.round((water_usage * 1000) / 4, 4)
return water_usage_liters_per_15min
[docs]
def calculate_chiller_power(max_cooling_cap, load, ambient_temp):
"""
Calculate the chiller power consumption based on load and operating conditions.
Obtained from:
1) https://github.com/NREL/EnergyPlus/blob/9bb39b77a871dee7543c892ae53b0812c4c17b0d/testfiles/AirCooledElectricChiller.idf
2) https://github.com/NREL/EnergyPlus/issues/763
3) https://dmey.github.io/EnergyPlusFortran-Reference/proc/calcelectricchillermodel.html
4) https://github.com/NREL/EnergyPlus/blob/9bb39b77a871dee7543c892ae53b0812c4c17b0d/tst/EnergyPlus/unit/ChillerElectric.unit.cc#L95
Args:
load (float): The heat load to be removed by the chiller (Watts).
ambient_temp (float): Current ambient temperature (Celsius).
Returns:
float: Estimated power consumption of the chiller (Watts).
"""
# Coefficients from https://github.com/NREL/EnergyPlus/blob/9bb39b77a871dee7543c892ae53b0812c4c17b0d/tst/EnergyPlus/unit/ChillerElectric.unit.cc#L95
capacity_coefficients = [0.94483600, -0.05700880, 0.00185486]
power_coefficients = [2.333, -1.975, 0.6121]
full_load_factor = [0.03303, 0.6852, 0.2818]
# Chiller design specifications
min_plr = 0.05
max_plr = 1.0
design_cond_temp = 35.0
design_evp_out_temp = 6.67
chiller_nominal_cap = max_cooling_cap
temp_rise_coef = 2.778
rated_cop = 3.0
# Calculate the delta temperature for capacity adjustment
delta_temp = (ambient_temp - design_cond_temp)/temp_rise_coef - (design_evp_out_temp - design_cond_temp)
# Calculate available nominal capacity ratio
avail_nom_cap_rat = capacity_coefficients[0] + capacity_coefficients[1] * delta_temp + capacity_coefficients[2] * delta_temp**2
# Calculate available chiller capacity
available_capacity = chiller_nominal_cap * avail_nom_cap_rat if avail_nom_cap_rat != 0 else 0
# Calculate power ratio
full_power_ratio = power_coefficients[0] + power_coefficients[1] * avail_nom_cap_rat + power_coefficients[2] * avail_nom_cap_rat**2
# Determine part load ratio (PLR)
part_load_ratio = max(min_plr, min(load / available_capacity, max_plr)) if available_capacity > 0 else 0
# Calculate fractional full load power
frac_full_load_power = full_load_factor[0] + full_load_factor[1] * part_load_ratio + full_load_factor[2] * part_load_ratio**2
# Determine operational part load ratio (OperPartLoadRat)
# If the PLR is less than Min PLR calculate the actual PLR for calculations. The power will then adjust for the cycling.
if available_capacity > 0:
if load / available_capacity < min_plr:
oper_part_load_rat = load / available_capacity
else:
oper_part_load_rat = part_load_ratio
else:
oper_part_load_rat = 0.0
# Operational PLR for actual conditions
if oper_part_load_rat < min_plr:
frac = min(1.0, oper_part_load_rat / min_plr)
else:
frac = 1.0
# Calculate the chiller compressor power
power = frac_full_load_power * full_power_ratio * available_capacity / rated_cop * frac
# Total heat rejection is the sum of the cooling capacity and the power input
total_heat_rejection = load + power
return power if oper_part_load_rat > 0 else 0
[docs]
def calculate_HVAC_power(CRAC_setpoint, avg_CRAC_return_temp, ambient_temp, data_center_full_load, DC_Config, ctafr=None):
"""Calculate the HVAV power attributes
Args:
CRAC_Setpoint (float): The control action
avg_CRAC_return_temp (float): The average of the temperatures from all the Racks + their corresponding return approach temperature (Delta)
ambient_temp (float): outside air temperature
data_center_full_load (float): total data center capacity
Returns:
CRAC_Fan_load (float): CRAC fan power
CT_Fan_pwr (float): Cooling tower fan power
CRAC_cooling_load (float): CRAC cooling load
Compressor_load (float): Chiller compressor load
"""
# Air system calculations
m_sys = DC_Config.RHO_AIR * DC_Config.CRAC_SUPPLY_AIR_FLOW_RATE_pu * data_center_full_load
CRAC_cooling_load = m_sys * DC_Config.C_AIR * max(0.0, avg_CRAC_return_temp - CRAC_setpoint)
CRAC_Fan_load = DC_Config.CRAC_FAN_REF_P * (DC_Config.CRAC_SUPPLY_AIR_FLOW_RATE_pu / DC_Config.CRAC_REFRENCE_AIR_FLOW_RATE_pu)**3
chiller_power = calculate_chiller_power(DC_Config.CT_FAN_REF_P, CRAC_cooling_load, ambient_temp)
# Chiller power calculation
power_consumed_CW = (DC_Config.CW_PRESSURE_DROP * DC_Config.CW_WATER_FLOW_RATE) / DC_Config.CW_PUMP_EFFICIENCY
# Chilled water pump power calculation
power_consumed_CT = (DC_Config.CT_PRESSURE_DROP*DC_Config.CT_WATER_FLOW_RATE)/DC_Config.CT_PUMP_EFFICIENCY
if ambient_temp < 5:
return CRAC_Fan_load, 0.0, CRAC_cooling_load, chiller_power, power_consumed_CW, power_consumed_CT
# Cooling tower fan power calculations
Cooling_tower_air_delta = max(50 - (ambient_temp - CRAC_setpoint), 1)
m_air = CRAC_cooling_load / (DC_Config.C_AIR * Cooling_tower_air_delta)
v_air = m_air / DC_Config.RHO_AIR
# Reference cooling tower air flow rate
if ctafr is None:
ctafr = DC_Config.CT_REFRENCE_AIR_FLOW_RATE
CT_Fan_pwr = DC_Config.CT_FAN_REF_P * (min(v_air / ctafr, 1))**3
# ToDo: exploring the new chiller_power method
return CRAC_Fan_load, CT_Fan_pwr, CRAC_cooling_load, chiller_power, power_consumed_CW, power_consumed_CT
[docs]
def chiller_sizing(DC_Config, min_CRAC_setpoint=16, max_CRAC_setpoint=22, max_ambient_temp=40.0):
'''
Calculates the chiller sizing for a data center based on the given configuration and parameters.
Parameters:
DC_Config (object): The data center configuration object.
min_CRAC_setpoint (float): The minimum CRAC setpoint temperature in degrees Celsius. Default is 16.
max_CRAC_setpoint (float): The maximum CRAC setpoint temperature in degrees Celsius. Default is 22.
max_ambient_temp (float): The maximum ambient temperature in degrees Celsius. Default is 40.0.
Returns:
tuple: A tuple containing the cooling tower reference air flow rate (ctafr) and the rated load of the cooling tower (CT_rated_load).
'''
dc = DataCenter_ITModel(num_racks=DC_Config.NUM_RACKS,
rack_supply_approach_temp_list=DC_Config.RACK_SUPPLY_APPROACH_TEMP_LIST,
rack_CPU_config=DC_Config.RACK_CPU_CONFIG,
max_W_per_rack=DC_Config.MAX_W_PER_RACK,
DC_ITModel_config=DC_Config)
cpu_load = 100.0
ITE_load_pct_list = [cpu_load for i in range(DC_Config.NUM_RACKS)]
rackwise_cpu_pwr, rackwise_itfan_pwr, rackwise_outlet_temp = \
dc.compute_datacenter_IT_load_outlet_temp(ITE_load_pct_list=ITE_load_pct_list, CRAC_setpoint=max_CRAC_setpoint)
avg_CRAC_return_temp = calculate_avg_CRAC_return_temp(rack_return_approach_temp_list=DC_Config.RACK_RETURN_APPROACH_TEMP_LIST,
rackwise_outlet_temp=rackwise_outlet_temp)
data_center_total_ITE_Load = sum(rackwise_cpu_pwr) + sum(rackwise_itfan_pwr)
m_sys = DC_Config.RHO_AIR * DC_Config.CRAC_SUPPLY_AIR_FLOW_RATE_pu * data_center_total_ITE_Load
CRAC_cooling_load = m_sys*DC_Config.C_AIR*max(0.0, avg_CRAC_return_temp-min_CRAC_setpoint)
Cooling_tower_air_delta = max(50 - (max_ambient_temp-min_CRAC_setpoint), 1)
m_air = CRAC_cooling_load/(DC_Config.C_AIR*Cooling_tower_air_delta)
# Cooling Tower Reference Air FlowRate
ctafr = m_air/DC_Config.RHO_AIR
'''
# Assuming that the HVAC consumes 43% of energy on a datacenter, and the rest is IT
# We assume that on months where temperature is lower than 15C, the HVAC energy consumption is almost 0. (> 60% of time)
# Also, the HVAC energy consumption follow the external temperature trend (almost a sin signal).
# The average area for a sin period of a sin is 2*pi
# The average power consumption of a day where the maximum temperature is raised and the lowest setpoint is used (MaxPower) is 2*pi*MaxPower [W]
# The average energy for that day is (2*pi*MaxPower)[W]/24[hours] -> 0.26 * MAxPower [W/h]
# So, to obtain an average HVAC energy consumption of 43%, we need to scale the total maximum energy consumption with a factor of 10
# This value is obtained after a methodic literature search.
'''
CT_rated_load = CRAC_cooling_load #2 * data_center_total_ITE_Load * (43/(100-43))
return ctafr, CT_rated_load
[docs]
def calculate_avg_CRAC_return_temp(rack_return_approach_temp_list,rackwise_outlet_temp):
"""Calculate the CRAC return air temperature
Args:
rack_return_approach_temp_list (List[float]): The delta change in temperature from each rack to the CRAC unit
rackwise_outlet_temp (float): The outlet temperature of each rack
Returns:
(float): CRAC return air temperature
"""
n = len(rack_return_approach_temp_list)
return sum([i + j for i,j in zip(rack_return_approach_temp_list,rackwise_outlet_temp)])/n # CRAC return is averaged across racks
"""
References:
[1]: Postema, Björn Frits. "Energy-efficient data centres: model-based analysis of power-performance trade-offs." (2018).
[2]: Raghunathan, S., & Vk, M. (2014). Power management using dynamic power state transitions and dynamic voltage frequency
scaling controls in virtualized server clusters. Turkish Journal of Electrical Engineering and Computer Sciences, 24(4). doi: 10.3906/elk-1403-264
[3]: Sun, Kaiyu, et al. "Prototype energy models for data centers." Energy and Buildings 231 (2021): 110603.
[4]: Breen, Thomas J., et al. "From chip to cooling tower data center modeling: Part I influence of server inlet temperature and temperature
rise across cabinet." 2010 12th IEEE Intersociety Conference on Thermal and Thermomechanical Phenomena in Electronic Systems. IEEE, 2010.
[5]: https://h2ocooling.com/blog/look-cooling-tower-fan-efficiences/#:~:text=The%20tower%20has%20been%20designed,of%200.42%20inches%20of%20water.
"""