3
3
4
4
def calculate_5yr_tco (config , user_inputs ):
5
5
"""
6
- Calculates 5-year TCO with correct initial CAPEX reporting .
7
- 초기 CAPEX 보고 로직이 수정된 버전입니다 .
6
+ Calculates 5-year TCO with detailed cost breakdowns for CAPEX and OPEX .
7
+ CAPEX와 OPEX에 대한 상세 비용 내역을 포함하여 5년 TCO를 계산합니다 .
8
8
"""
9
9
demand_profile = user_inputs ['demand_profile' ]
10
10
energy_mix = user_inputs ['energy_mix' ]
@@ -15,7 +15,7 @@ def calculate_5yr_tco(config, user_inputs):
15
15
asset_lifetime = config .get ('asset_lifetime_years' , 20 )
16
16
discount_rate = econ_assumptions ['discount_rate' ]
17
17
18
- # --- 1. Calculate Initial CAPEX (at t=0) ---
18
+ # --- 1. Calculate Initial CAPEX (at t=0) and details ---
19
19
initial_capex = 0
20
20
capex_details = {}
21
21
peak_demand_kw_yr1 = demand_profile ['peak_demand_mw' ].iloc [0 ] * 1000
@@ -27,8 +27,9 @@ def calculate_5yr_tco(config, user_inputs):
27
27
capex_details [f"{ source } _capex" ] = capex
28
28
initial_capex += capex
29
29
30
- # --- 2. Calculate PV of all costs over the simulation period ---
30
+ # --- 2. Initialize variables for loop ---
31
31
results = []
32
+ opex_breakdown_pv = {}
32
33
total_opex_pv = 0
33
34
total_replacement_capex_pv = 0
34
35
total_demand_pv_kwh = 0
@@ -43,7 +44,7 @@ def calculate_5yr_tco(config, user_inputs):
43
44
annual_capex_replacement = 0
44
45
fc_params = tech_params .get ('hydrogen_SOFC' , {})
45
46
stack_lifetime = fc_params .get ('stack_lifetime_years' , 4 )
46
- if ( simulation_year - 1 ) > 0 and (simulation_year - 1 ) % stack_lifetime == 0 and energy_mix .get ('hydrogen_SOFC' , 0 ) > 0 :
47
+ if simulation_year > 1 and (simulation_year - 1 ) % stack_lifetime == 0 and energy_mix .get ('hydrogen_SOFC' , 0 ) > 0 :
47
48
fc_capacity_kw = peak_demand_kw_yr1 * (energy_mix ['hydrogen_SOFC' ] / 100 )
48
49
replacement_rate = fc_params .get ('stack_replacement_cost_rate' , 0.4 )
49
50
replacement_cost = (fc_capacity_kw * fc_params .get ('capex_per_kw' , 0 )) * replacement_rate
@@ -52,61 +53,67 @@ def calculate_5yr_tco(config, user_inputs):
52
53
53
54
total_replacement_capex_pv += annual_capex_replacement * discount_factor
54
55
55
- # Annual OPEX
56
- # ... (OPEX calculation logic remains the same)
57
- annual_opex = 0
58
- total_emissions_kg = 0
56
+ # Annual OPEX Calculation
57
+ opex_details_annual = {}
59
58
initial_grid_price = scenario_params .get ('grid_price_per_kwh' , 0.12 )
60
59
initial_ng_price = scenario_params .get ('gas_fuel_cost_per_kwh' , 0.08 )
60
+
61
61
grid_kwh = annual_demand_kwh * (energy_mix .get ('grid' , 0 ) / 100 )
62
- grid_price = initial_grid_price * ((1 + econ_assumptions ['grid_escalation' ]) ** (simulation_year - 1 ))
63
- annual_opex += grid_kwh * grid_price
64
- total_emissions_kg += grid_kwh * tech_params .get ('grid' , {}).get ('carbon_emission_factor' , 0 )
62
+ opex_details_annual ['grid_purchase_cost' ] = grid_kwh * (initial_grid_price * ((1 + econ_assumptions ['grid_escalation' ]) ** (simulation_year - 1 )))
63
+
64
+ total_emissions_kg = grid_kwh * tech_params .get ('grid' , {}).get ('carbon_emission_factor' , 0 )
65
+
66
+ opex_details_annual ['o&m_cost' ] = 0
67
+ opex_details_annual ['h2_fuel_cost' ] = 0
68
+ opex_details_annual ['ng_fuel_cost' ] = 0
69
+
65
70
for source in ['solar' , 'wind' , 'hydrogen_SOFC' , 'NG_SOFC' ]:
66
71
if energy_mix .get (source , 0 ) > 0 :
67
72
source_params = tech_params .get (source , {})
68
73
capacity_kw = peak_demand_kw_yr1 * (energy_mix [source ] / 100 )
69
- annual_opex += (capacity_kw * source_params .get ('capex_per_kw' , 0 )) * source_params .get ('opex_rate' , 0.015 )
74
+ opex_details_annual ['o&m_cost' ] += (capacity_kw * source_params .get ('capex_per_kw' , 0 )) * source_params .get ('opex_rate' , 0.015 )
75
+
70
76
if source == 'hydrogen_SOFC' :
71
- energy_kwh = annual_demand_kwh * (energy_mix [source ] / 100 )
72
- fuel_cost = econ_assumptions ['h2_fuel_cost' ] * ((1 + econ_assumptions ['fuel_escalation' ]) ** (simulation_year - 1 ))
73
- annual_opex += energy_kwh * fuel_cost
77
+ opex_details_annual ['h2_fuel_cost' ] += (annual_demand_kwh * (energy_mix [source ] / 100 )) * (econ_assumptions ['h2_fuel_cost' ] * ((1 + econ_assumptions ['fuel_escalation' ]) ** (simulation_year - 1 )))
74
78
elif source == 'NG_SOFC' :
75
- energy_kwh = annual_demand_kwh * (energy_mix [source ] / 100 )
76
- fuel_cost = initial_ng_price * ((1 + econ_assumptions ['fuel_escalation' ]) ** (simulation_year - 1 ))
77
- annual_opex += energy_kwh * fuel_cost
78
- total_emissions_kg += energy_kwh * source_params .get ('carbon_emission_factor' , 0 )
79
+ ng_kwh = annual_demand_kwh * (energy_mix [source ] / 100 )
80
+ opex_details_annual ['ng_fuel_cost' ] += ng_kwh * (initial_ng_price * ((1 + econ_assumptions ['fuel_escalation' ]) ** (simulation_year - 1 )))
81
+ total_emissions_kg += ng_kwh * source_params .get ('carbon_emission_factor' , 0 )
82
+
83
+ opex_details_annual ['carbon_tax_cost' ] = 0
79
84
if econ_assumptions ['carbon_tax_year' ] and simulation_year >= econ_assumptions ['carbon_tax_year' ]:
80
- annual_opex += (total_emissions_kg / 1000 ) * econ_assumptions ['carbon_tax_price' ]
85
+ opex_details_annual ['carbon_tax_cost' ] = (total_emissions_kg / 1000 ) * econ_assumptions ['carbon_tax_price' ]
86
+
87
+ annual_opex = sum (opex_details_annual .values ())
81
88
89
+ # Accumulate Present Values
82
90
total_opex_pv += annual_opex * discount_factor
83
91
total_demand_pv_kwh += annual_demand_kwh * discount_factor
92
+ for key , value in opex_details_annual .items ():
93
+ opex_breakdown_pv [key ] = opex_breakdown_pv .get (key , 0 ) + (value * discount_factor )
84
94
85
95
results .append ({'year' : actual_year , 'annual_opex' : annual_opex })
86
96
87
97
# --- 3. Final Summary Calculation ---
88
- total_capex_pv_for_tco = initial_capex + total_replacement_capex_pv
89
- tco_5yr = total_capex_pv_for_tco + total_opex_pv
98
+ total_capex_pv = initial_capex + total_replacement_capex_pv
99
+ tco_5yr = total_capex_pv + total_opex_pv
90
100
91
- # For LCOE, we need to annualize the total investment over the asset lifetime
101
+ # For LCOE, annualize the total investment over the asset lifetime
92
102
if discount_rate > 0 :
93
103
crf = (discount_rate * (1 + discount_rate ) ** asset_lifetime ) / ((1 + discount_rate ) ** asset_lifetime - 1 )
94
104
else :
95
105
crf = 1 / asset_lifetime
96
- annualized_total_investment = total_capex_pv_for_tco * crf
97
-
98
- # Average annual OPEX (PV)
99
- avg_annual_opex_pv = total_opex_pv / len (demand_profile )
106
+ annualized_total_investment = total_capex_pv * crf
100
107
101
- # Average annual demand (PV )
102
- avg_annual_demand_pv_kwh = total_demand_pv_kwh / len ( demand_profile )
108
+ avg_annual_opex_pv = total_opex_pv / config . get ( 'simulation_period_years' , 5 )
109
+ avg_annual_demand_pv_kwh = total_demand_pv_kwh / config . get ( 'simulation_period_years' , 5 )
103
110
104
111
summary = {
105
112
'5_Year_TCO' : tco_5yr ,
106
113
'Total_Initial_CAPEX' : initial_capex ,
107
- 'Total_CAPEX_PV' : total_capex_pv_for_tco ,
114
+ 'Total_CAPEX_PV' : total_capex_pv ,
108
115
'Total_OPEX_PV' : total_opex_pv ,
109
116
'LCOE_Avg_5yr' : ((annualized_total_investment + avg_annual_opex_pv ) / avg_annual_demand_pv_kwh ) * 1000 if avg_annual_demand_pv_kwh > 0 else 0
110
117
}
111
118
112
- return pd .DataFrame (results ), summary , capex_details , {}
119
+ return pd .DataFrame (results ), summary , capex_details , opex_breakdown_pv
0 commit comments