Skip to content

Commit da3eadc

Browse files
committed
initial data center model
1 parent 3b7c6a2 commit da3eadc

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import numpy as np
2+
from attrs import field, define
3+
4+
from h2integrate.core.utilities import BaseConfig, merge_shared_inputs
5+
from h2integrate.core.validators import gt_zero, gte_zero
6+
from h2integrate.core.model_baseclasses import (
7+
CostModelBaseClass,
8+
CostModelBaseConfig,
9+
PerformanceModelBaseClass,
10+
)
11+
12+
13+
@define(kw_only=True)
14+
class DataCenterPerformanceConfig(BaseConfig):
15+
"""
16+
Configuration class for the DataCenterPerformanceModel.
17+
"""
18+
19+
system_capacity_mw: float = field(validator=gt_zero)
20+
compute_electrical_efficiency: float = field(validator=gt_zero)
21+
cooling_load_ratio: float = field(validator=gte_zero)
22+
water_use_per_mwh: float = field(validator=gte_zero)
23+
24+
25+
class DataCenterPerformanceModel(PerformanceModelBaseClass):
26+
27+
def initialize(self):
28+
super().initialize()
29+
self.commodity = "compute_load"
30+
self.commodity_rate_units = "kW"
31+
self.commodity_amount_units = "kW*h"
32+
33+
def setup(self):
34+
super().setup()
35+
n_timesteps = self.options["plant_config"]["plant"]["simulation"]["n_timesteps"]
36+
self.config = DataCenterPerformanceConfig.from_dict(
37+
merge_shared_inputs(self.options["tech_config"]["model_inputs"], "performance"),
38+
additional_cls_name=self.__class__.__name__,
39+
)
40+
41+
self.add_input(
42+
f"{self.commodity}_demand",
43+
val=0.0,
44+
shape=n_timesteps,
45+
units=self.commodity_rate_units,
46+
desc=f"Data center compute load demand profile",
47+
)
48+
49+
self.add_input(
50+
"electricity_in",
51+
val=0.0,
52+
shape=n_timesteps,
53+
units="MW/h",
54+
desc="Electricity input",
55+
)
56+
57+
self.add_input(
58+
"water_in",
59+
val=0.0,
60+
shape=n_timesteps,
61+
units="galUS/h",
62+
desc="Water input",
63+
)
64+
65+
self.add_output(
66+
"water_consumed",
67+
val=0.0,
68+
shape=n_timesteps,
69+
units="galUS/h",
70+
desc="Water consumed by the plant",
71+
)
72+
73+
def compute(self, inputs, outputs):
74+
"""
75+
Computation for the OM component.
76+
77+
For a template class this is not implement and raises an error.
78+
"""
79+
system_capacity = inputs["system_capacity"] # plant capacity in MW
80+
81+
# compute load demand, saturated at maximum rated system capacity
82+
compute_load_demand = np.where(
83+
inputs["compute_load_demand"] > system_capacity,
84+
system_capacity,
85+
inputs["compute_load_demand"],
86+
)
87+
88+
electrical_compute_load_demand = (
89+
compute_load_demand / self.config.compute_electrical_efficiency
90+
)
91+
92+
# Total electricity demand is the summation of compute load and cooling load
93+
total_electricity_demand = (
94+
electrical_compute_load_demand
95+
+ electrical_compute_load_demand * self.config.cooling_load_ratio
96+
)
97+
98+
# available electricity, saturated at maximum rated system capacity
99+
electricity_available = np.where(
100+
inputs["electricity_in"] > total_electricity_demand,
101+
system_capacity,
102+
inputs["electricity_in"],
103+
)
104+
105+
electricity_used = np.minimum.reduce([total_electricity_demand, electricity_available])
106+
107+
water_used = electrical_compute_load_demand * self.config.water_use_per_mwh
108+
109+
outputs["unmet_electricity_demand"] = total_electricity_demand - electricity_used
110+
outputs["water_consumed"] = water_used
111+
112+
113+
@define(kw_only=True)
114+
class DataCenterCostConfig(CostModelBaseConfig):
115+
"""
116+
Configuration class for the DataCenterCostModel.
117+
"""
118+
119+
system_capacity_mw: float = field(validator=gt_zero)
120+
capex_per_mw: float | int = field(validator=gte_zero)
121+
fixed_opex_per_mw_per_year: float | int = field(validator=gte_zero)
122+
variable_opex_per_mwh: float | int = field(validator=gte_zero)
123+
124+
125+
class DataCenterCostModel(CostModelBaseClass):
126+
127+
def initialize(self):
128+
super().initialize()
129+
self.commodity = "compute_load"
130+
self.commodity_rate_units = "kW"
131+
self.commodity_amount_units = "kW*h"
132+
133+
def setup(self):
134+
super().setup()
135+
self.config = DataCenterCostConfig.from_dict(
136+
merge_shared_inputs(self.options["tech_config"]["model_inputs"], "cost"),
137+
additional_cls_name=self.__class__.__name__,
138+
)
139+
n_timesteps = self.options["plant_config"]["plant"]["simulation"]["n_timesteps"]
140+
141+
self.add_input(
142+
"system_capacity",
143+
val=self.config.system_capacity_mw,
144+
units="MW",
145+
desc="Data center capacity",
146+
)
147+
self.add_input(
148+
"compute_load_out",
149+
val=0.0,
150+
shape=n_timesteps,
151+
units="MW",
152+
desc="Hourly compute load output from performance model",
153+
)
154+
self.add_input(
155+
"capex_per_mw",
156+
val=self.config.capex_per_mw,
157+
units="USD/MW",
158+
desc="Capital cost per unit capacity",
159+
)
160+
self.add_input(
161+
"fixed_opex_per_mw_per_year",
162+
val=self.config.fixed_opex_per_mw_per_year,
163+
units="USD/(MW*year)",
164+
desc="Fixed operating expenses per unit capacity per year",
165+
)
166+
self.add_input(
167+
"variable_opex_per_mwh",
168+
val=self.config.variable_opex_per_mwh,
169+
units="USD/(MW*h)",
170+
desc="Variable operating expenses per unit generation",
171+
)
172+
173+
def compute(self, inputs, outputs):
174+
"""
175+
Compute capital and operating costs for the data center.
176+
"""
177+
plant_capacity_kw = inputs["system_capacity"]
178+
compute_load_out = inputs["compute_load_out"] # MW hourly profile
179+
capex_per_mw = inputs["capex_per_mw"]
180+
fixed_opex_per_mw_per_year = inputs["fixed_opex_per_mw_per_year"]
181+
variable_opex_per_mwh = inputs["variable_opex_per_mwh"]
182+
183+
# Sum hourly compute load output to get annual generation
184+
# compute_load_out is in MW, so sum gives MWh for hourly data
185+
dt = self.options["plant_config"]["plant"]["simulation"]["dt"]
186+
delivered_compute_load_MWdt = compute_load_out.sum()
187+
delivered_compute_load_MWh = delivered_compute_load_MWdt * dt / 3600
188+
189+
# Calculate capital expenditure
190+
capex = capex_per_mw * plant_capacity_kw
191+
192+
# Calculate fixed operating expenses over project life
193+
fixed_om = fixed_opex_per_mw_per_year * plant_capacity_kw
194+
195+
# Calculate variable operating expenses over project life
196+
variable_om = variable_opex_per_mwh * delivered_compute_load_MWh
197+
198+
# Total operating expenditure includes all O&M
199+
opex = fixed_om + variable_om
200+
201+
outputs["CapEx"] = capex
202+
outputs["OpEx"] = opex

0 commit comments

Comments
 (0)