@@ -48,6 +48,16 @@ function execute_dispatch_economic_projection(
4848 downselection_mode= downselection_mode
4949 )
5050
51+ if run_mode == " forecast"
52+ all_grc_results, system_portfolios = forecast_dispatch_novel_units (
53+ settings,
54+ unit_specs,
55+ all_grc_results,
56+ all_price_results,
57+ system_portfolios,
58+ )
59+ end
60+
5161 long_econ_results, dispatch_results = postprocess_results (
5262 CLI_args,
5363 settings,
@@ -1480,6 +1490,103 @@ function summarize_dispatch_results(settings, unit_specs, long_econ_results)
14801490end
14811491
14821492
1493+ function forecast_dispatch_novel_units (
1494+ settings,
1495+ unit_specs,
1496+ all_grc_results,
1497+ all_price_results,
1498+ system_portfolios,
1499+ )
1500+ supplemented_grc = deepcopy (all_grc_results)
1501+ px = deepcopy (all_price_results)
1502+ allowed = settings[" scenario" ][" allowed_xtr_types" ]
1503+
1504+ for t in unique (all_grc_results[! , :y ])
1505+ year_grc_data = filter (:y => y -> y == t, supplemented_grc)
1506+ year_px_data = filter (:y => y -> y == t, px)
1507+
1508+ for unit_type in allowed
1509+ if ! in (unit_type, unique (year_grc_data[! , :unit_type ]))
1510+ udata = filter (:unit_type => u -> u == unit_type, unit_specs)
1511+ uMC = udata[1 , :FC_per_MWh ] + udata[1 , :VOM ]
1512+
1513+ # Assumption: novel units dispatch at max PL * (1-CF) whenever
1514+ # they screen in to the market clearing price
1515+ ugen = udata[1 , :capacity ] * udata[1 , :capacity_factor ]
1516+
1517+ # Create a column in the prices df containing the generation
1518+ # curve of the unit type
1519+ transform! (
1520+ year_px_data,
1521+ [:lambda ]
1522+ => ((lambda) -> ifelse .((lambda .>= uMC), ugen, 0 ))
1523+ => Symbol (unit_type)
1524+ )
1525+
1526+ # Add 1 dummy unit of this type to the system portfolio
1527+ unit_pf_row = [
1528+ unit_type,
1529+ 1 , # num_units
1530+ 0.0 , # real
1531+ 0.0 , # autoexpansion
1532+ udata[1 , :capacity ],
1533+ udata[1 , :capacity_factor ],
1534+ udata[1 , :capacity ], # total capacity (1 x cap)
1535+ 0.0 , # agent_total_capacity
1536+ ugen, # total_derated_capacity
1537+ 0.0 , # agent_cap_frac
1538+ ugen, # total_esc_der_cap
1539+ udata[1 , :capacity ], # total_esc_cap
1540+ 1 , # esc_num_units
1541+ ]
1542+ push! (system_portfolios[t], unit_pf_row)
1543+ end
1544+ end
1545+
1546+ # Select relevant columns from prices df and reshape to match grc
1547+ novel_types = setdiff (
1548+ names (year_px_data),
1549+ [" y" , " d" , " h" , " lambda" , " reg_rmp" , " spin_rmp" , " nspin_rmp" ],
1550+ )
1551+
1552+ year_supp_grc = stack (
1553+ year_px_data,
1554+ novel_types
1555+ )
1556+
1557+ # Convert column names to match grc format
1558+ year_supp_grc = rename (year_supp_grc, :variable => :unit_type )
1559+ year_supp_grc = rename (year_supp_grc, :value => :gen )
1560+
1561+ # Remove unneeded price columns
1562+ year_supp_grc = select (year_supp_grc, Not ([:lambda , :reg_rmp , :spin_rmp , :nspin_rmp ]))
1563+
1564+ # Fill out other grc columns
1565+ year_supp_grc[! , :reg ] .= 0.0
1566+ year_supp_grc[! , :spin ] .= 0.0
1567+ year_supp_grc[! , :nspin ] .= 0.0
1568+
1569+ # commitment
1570+ transform! (
1571+ year_supp_grc,
1572+ [:gen ]
1573+ => ((gen) -> ifelse .((gen .> 0.0 ), 1 , 0 ))
1574+ => :commit
1575+ )
1576+
1577+ year_supp_grc[! , :su ] .= 0
1578+ year_supp_grc[! , :sd ] .= 0
1579+
1580+ supplemented_grc = vcat (supplemented_grc, year_supp_grc)
1581+
1582+ end
1583+
1584+
1585+ return supplemented_grc, system_portfolios
1586+
1587+ end
1588+
1589+
14831590function postprocess_results (
14841591 CLI_args,
14851592 settings,
0 commit comments