Skip to content

Commit

Permalink
Enable thermal power plants to burn multiple fuels (#586)
Browse files Browse the repository at this point in the history
This feature enables thermal generators to burn multiple fuels at the same time and also allows the user to specify the co-firing level during both startup and generation processes to meet potential regulations (e.g., H2 blending requirements in combustion turbines proposed by EPA). This feature also allows users to specify heat rates for different fuels in case there is any efficiency penalty due to co-firing.
  • Loading branch information
qluo0320github authored Feb 20, 2024
1 parent 448d724 commit 744896c
Show file tree
Hide file tree
Showing 55 changed files with 879 additions and 159 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
piecewise-linear approximation of heat rate curves.
Adds a CO2 module that determines the CO2 emissions based on fuel consumption, CO2 capture
fraction, and whether the feedstock is biomass.
- Enable thermal power plants to burn multiple fuels (#586)
- Feature electrolysis basic (#525)
Adds hydrogen electrolyzer model which enables the addition of hydrogen electrolyzer
demands along with optional clean supply constraints.
Expand Down Expand Up @@ -81,7 +82,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The matrix-style input of the grid for Network.csv is deprecated in favor a column-style input.
Instead of columns z1, z2, ... with entries -1, 0, 1, use two columns: Start_Zone, End_Zone (#591).


## [0.3.6] - 2023-08-01

### Fixed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
,Network_zones,CO_2_Cap_Zone_1,CO_2_Max_tons_MWh_1,CO_2_Max_Mtons_1
NE,z1,1,0.05,0.018
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
,Network_zones,CapRes_1
NE,z1,0.156
25 changes: 25 additions & 0 deletions Example_Systems/SmallNewEngland/OneZone_MultiFuels/Demand_data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Voll,Demand_Segment,Cost_of_Demand_Curtailment_per_MW,Max_Demand_Curtailment,Rep_Periods,Timesteps_per_Rep_Period,Sub_Weights,Time_Index,Demand_MW_z1
50000,1,1,1,1,24,8760,1,11162
,,,,,,,2,10556
,,,,,,,3,10105
,,,,,,,4,9878
,,,,,,,5,9843
,,,,,,,6,10017
,,,,,,,7,10390
,,,,,,,8,10727
,,,,,,,9,11298
,,,,,,,10,11859
,,,,,,,11,12196
,,,,,,,12,12321
,,,,,,,13,12381
,,,,,,,14,12270
,,,,,,,15,12149
,,,,,,,16,12219
,,,,,,,17,13410
,,,,,,,18,14539
,,,,,,,19,14454
,,,,,,,20,14012
,,,,,,,21,13494
,,,,,,,22,12772
,,,,,,,23,11877
,,,,,,,24,10874
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
,Network_zones,ESR_1,ESR_2
NE,z1,0.259,0.348
26 changes: 26 additions & 0 deletions Example_Systems/SmallNewEngland/OneZone_MultiFuels/Fuels_data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Time_Index,NG,None,H2
0,0.05306,0,0
1,5.28,0,5
2,5.28,0,5
3,5.28,0,5
4,5.28,0,5
5,5.28,0,5
6,5.28,0,5
7,5.28,0,5
8,5.28,0,5
9,5.28,0,5
10,5.28,0,5
11,5.28,0,5
12,5.28,0,5
13,5.28,0,5
14,5.28,0,5
15,5.28,0,5
16,5.28,0,5
17,5.28,0,5
18,5.28,0,5
19,5.28,0,5
20,5.28,0,5
21,5.28,0,5
22,5.28,0,5
23,5.28,0,5
24,5.28,0,5
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Time_Index,natural_gas_combined_cycle,solar_pv,onshore_wind,battery
1,1,0,0.889717042,1
2,1,0,0.877715468,1
3,1,0,0.903424203,1
4,1,0,0.895153165,1
5,1,0,0.757258117,1
6,1,0,0.630928695,1
7,1,0,0.557177782,1
8,1,0,0.6072492,1
9,1,0.1779,0.423417866,1
10,1,0.429,0.007470775,1
11,1,0.5748,0.002535942,1
12,1,0.6484,0.002153709,1
13,1,0.6208,0.00445132,1
14,1,0.596,0.007711587,1
15,1,0.5013,0.100848213,1
16,1,0.3311,0.201802149,1
17,1,0.0642,0.141933054,1
18,1,0,0.567022562,1
19,1,0,0.946024895,1
20,1,0,0.923394203,1
21,1,0,0.953386247,1
22,1,0,0.929205418,1
23,1,0,0.849528909,1
24,1,0,0.665570974,1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MaxCapReqConstraint,ConstraintDescription,Max_MW
1,PV,50000
2,Wind,100000
3,Batteries,60000
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MinCapReqConstraint,ConstraintDescription,Min_MW
1,PV,5000
2,Wind,10000
3,Batteries,6000
15 changes: 15 additions & 0 deletions Example_Systems/SmallNewEngland/OneZone_MultiFuels/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Small New England: One Zone with resources that can use multiple fuels

**SmallNewEngland** is set of a simplified versions of the more detailed example system RealSystemExample. It is condensed for easy comprehension and quick testing of different components of the GenX. **SmallNewEngland/OneZone_MultiFules** is one of our most basic models, a 24-hour example with hourly resolution containing only one zone representing New England. The model includes only natural gas (cofiring with H2), solar PV, wind, and lithium-ion battery storage with no initial capacity.

To run the model, first navigate to the example directory at `GenX/Example_Systems/SmallNewEngland/OneZone_MultiFuels`:

`cd("Example_Systems/SmallNewEngland/OneZone_MultiFuels")`

Next, ensure that your settings in `GenX_settings.yml` are correct. The default settings use the solver HiGHS (`Solver: HiGHS`). Other optional policies include minimum capacity requirements, a capacity reserve margin, and more.

Once the settings are confirmed, run the model with the `Run.jl` script in the example directory:

`include("Run.jl")`

Once the model has completed, results will write to the `Results` directory.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Reg_Req_Percent_Demand,Reg_Req_Percent_VRE,Rsv_Req_Percent_Demand,Rsv_Req_Percent_VRE,Unmet_Rsv_Penalty_Dollar_per_MW,Dynamic_Contingency,Static_Contingency_MW
0.01,0.0032,0.033,0.0795,1000,0,0
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Resource,Derating_Factor_1
natural_gas_combined_cycle,0.93
solar_pv,0.8
onshore_wind,0.8
battery,0.95
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Resource,ESR_1,ESR_2
solar_pv,1,1
onshore_wind,1,1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Resource,Min_Cap_1,Min_Cap_2,Min_Cap_3
solar_pv,1,0,0
onshore_wind,0,1,0
battery,0,0,1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Resource,Min_Cap_1,Min_Cap_2,Min_Cap_3
solar_pv,1,0,0
onshore_wind,0,1,0
battery,0,0,1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Resource,Zone,Model,New_Build,Min_Cap_MW,Min_Cap_MWh,Inv_Cost_per_MWyr,Inv_Cost_per_MWhyr,Fixed_OM_Cost_per_MWyr,Fixed_OM_Cost_per_MWhyr,Var_OM_Cost_per_MWh,Var_OM_Cost_per_MWh_In,Self_Disch,Eff_Up,Eff_Down,Min_Duration,Max_Duration,region,cluster,existing_cap_mw,existing_cap_mwh
battery,1,1,1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,NE,0,0,0
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Resource,Zone,Model,MULTI_FUELS,New_Build,Min_Cap_MW,Min_Cap_MWh,Min_Charge_Cap_MW,Inv_Cost_per_MWyr,Fixed_OM_Cost_per_MWyr,Var_OM_Cost_per_MWh,Heat_Rate_MMBTU_per_MWh,Fuel,Cap_Size,Start_Cost_per_MW,Start_Fuel_MMBTU_per_MW,Up_Time,Down_Time,Ramp_Up_Percentage,Ramp_Dn_Percentage,Hydro_Energy_to_Power_Ratio,Min_Power,Reg_Max,Rsv_Max,MGA,Resource_Type,region,cluster,Num_Fuels,Fuel1,Heat_Rate1_MMBTU_per_MWh,Fuel1_Min_Cofire_Level,Fuel1_Max_Cofire_Level,Fuel1_Min_Cofire_Level_Start,Fuel1_Max_Cofire_Level_Start,Fuel2,Heat_Rate2_MMBTU_per_MWh,Fuel2_Min_Cofire_Level,Fuel2_Max_Cofire_Level,Fuel2_Min_Cofire_Level_Start,Fuel2_Max_Cofire_Level_Start,Existing_cap_mw
natural_gas_combined_cycle,1,1,1,1,0,0,0,65400,10287,3.55,7.43,NG,250,91,2,6,6,0.64,0.64,0,0.468,0.25,0.5,1,natural_gas_fired_combined_cycle,NE,1,2,NG,7.43,0.5,1,0,1,H2,7.43,0.5,1,0.05,1,0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Resource,Zone,Num_VRE_Bins,New_Build,Can_Retire,Min_Cap_MW,Min_Cap_MWh,Min_Charge_Cap_MW,Inv_Cost_per_MWyr,Fixed_OM_Cost_per_MWyr,Var_OM_Cost_per_MWh,Heat_Rate_MMBTU_per_MWh,MGA,Resource_Type,region,cluster,existing_cap_mw
solar_pv,1,1,1,0,0,0,0,85300,18760,0,9.13,1,solar_photovoltaic,NE,1,0
onshore_wind,1,1,1,0,0,0,0,97200,43205,0.1,9.12,1,onshore_wind_turbine,NE,1,0
3 changes: 3 additions & 0 deletions Example_Systems/SmallNewEngland/OneZone_MultiFuels/Run.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using GenX

run_genx_case!(dirname(@__FILE__))
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# CPLEX Solver Parameters
Feasib_Tol: 1.0e-05 # Constraint (primal) feasibility tolerances.
Optimal_Tol: 1e-5 # Dual feasibility tolerances.
Pre_Solve: 1 # Controls presolve level.
TimeLimit: 110000 # Limits total time solver.
MIPGap: 1e-3 # Relative (p.u. of optimal) mixed integer optimality tolerance for MIP problems (ignored otherwise).
Method: 2 # Algorithm used to solve continuous models (including MIP root relaxation).
BarConvTol: 1.0e-08 # Barrier convergence tolerance (determines when barrier terminates).
NumericFocus: 0 # Numerical precision emphasis.
SolutionType: 2 # Solution type for LP or QP.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
OverwriteResults: 0 # Overwrite existing results in output folder or create a new one; 0 = create new folder; 1 = overwrite existing results
PrintModel: 0 # Write the model formulation as an output; 0 = active; 1 = not active
NetworkExpansion: 0 # Transmission network expansionl; 0 = not active; 1 = active systemwide
Trans_Loss_Segments: 1 # Number of segments used in piecewise linear approximation of transmission losses; 1 = linear, >2 = piecewise quadratic
Reserves: 0 # Regulation (primary) and operating (secondary) reserves; 0 = not active, 1 = active systemwide
EnergyShareRequirement: 1 # Minimum qualifying renewables penetration; 0 = not active; 1 = active systemwide
CapacityReserveMargin: 1 # Number of capacity reserve margin constraints; 0 = not active; 1 = active systemwide
CO2Cap: 0 # CO2 emissions cap; 0 = not active (no CO2 emission limit); 1 = mass-based emission limit constraint; 2 = demand + rate-based emission limit constraint; 3 = generation + rate-based emission limit constraint
StorageLosses: 1 # Energy Share Requirement and CO2 constraints account for energy lost; 0 = not active (DO NOT account for energy lost); 1 = active systemwide (DO account for energy lost)
MinCapReq: 1 # Activate minimum technology carveout constraints; 0 = not active; 1 = active
MaxCapReq: 1 # Activate maximum technology carveout constraints; 0 = not active; 1 = active
ParameterScale: 1 # Turn on parameter scaling wherein load, capacity and power variables are defined in GW rather than MW. 0 = not active; 1 = active systemwide
WriteShadowPrices: 1 # Write shadow prices of LP or relaxed MILP; 0 = not active; 1 = active
UCommit: 2 # Unit committment of thermal power plants; 0 = not active; 1 = active using integer clestering; 2 = active using linearized clustering
TimeDomainReductionFolder: "TDR_Results" # Directory name where results from time domain reduction will be saved. If results already exist here, these will be used without running time domain reduction script again.
TimeDomainReduction: 0 # Time domain reduce (i.e. cluster) inputs based on Demand_data.csv, Generators_variability.csv, and Fuels_data.csv; 0 = not active (use input data as provided); 0 = active (cluster input data, or use data that has already been clustered)
ModelingToGenerateAlternatives: 0 # Modeling to generate alternatives; 0 = not active; 1 = active. Note: produces a single solution as output
ModelingtoGenerateAlternativeSlack: 0.1 # Slack value as a fraction of least-cost objective in budget constraint used for evaluating alternative model solutions; positive float value
ModelingToGenerateAlternativeIterations: 3 # Number of MGA iterations with maximization and minimization objective
MethodofMorris: 0 #Flag for turning on the Method of Morris analysis
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Gurobi Solver Parameters
# Common solver settings
Feasib_Tol: 1.0e-05 # Constraint (primal) feasibility tolerances.
Optimal_Tol: 1e-5 # Dual feasibility tolerances.
TimeLimit: 110000 # Limits total time solver.
Pre_Solve: 1 # Controls presolve level.
Method: 2 # Algorithm used to solve continuous models (including MIP root relaxation).

#Gurobi-specific solver settings
MIPGap: 1e-3 # Relative (p.u. of optimal) mixed integer optimality tolerance for MIP problems (ignored otherwise).
BarConvTol: 1.0e-08 # Barrier convergence tolerance (determines when barrier terminates).
NumericFocus: 0 # Numerical precision emphasis.
Crossover: 0 # Barrier crossver strategy.
PreDual: 0 # Decides whether presolve should pass the primal or dual linear programming problem to the LP optimization algorithm.
AggFill: 10 # Allowed fill during presolve aggregation.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# HiGHS Solver Parameters
# Common solver settings
Feasib_Tol: 1.0e-05 # Primal feasibility tolerance # [type: double, advanced: false, range: [1e-10, inf], default: 1e-07]
Optimal_Tol: 1.0e-05 # Dual feasibility tolerance # [type: double, advanced: false, range: [1e-10, inf], default: 1e-07]
TimeLimit: 1.0e23 # Time limit # [type: double, advanced: false, range: [0, inf], default: inf]
Pre_Solve: choose # Presolve option: "off", "choose" or "on" # [type: string, advanced: false, default: "choose"]
Method: choose #HiGHS-specific solver settings # Solver option: "simplex", "choose" or "ipm" # [type: string, advanced: false, default: "choose"]

#highs-specific solver settings

# run the crossover routine for ipx
# [type: string, advanced: "on", range: {"off", "on"}, default: "off"]
run_crossover: "on"
26 changes: 26 additions & 0 deletions docs/src/data_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,32 @@ This file contains the time-series of capacity factors / availability of the win

• Second column onwards: Resources are listed from the second column onward with headers matching each resource name in the `Vre_stor.csv` files in any order. The availability for each resource at each time step is defined as a fraction of installed capacity and should be between 0 and 1. Note that for this reason, resource names specified in all the resource `.csv` files must be unique.

#### 2.2.12 Settings-specific columns in the Generators\_data.csv to use multi fuels

This file contains additional setting and performance parameters for specifically thermal resources that use multiple fuels.
These variables must be explicitly listed in the `Generators_data.csv`.

###### Table 17: Settings-specific columns in the Generators\_data.csv file to use multi fuels
|**Column Name** | **Description**|
| :------------ | :-----------|
|**Technology type flags**|
|MULTI_FUELS | {0, 1}, Flag to indicate membership in set of thermal resources that can burn multiple fuels at the same time (e.g., natural gas combined cycle cofiring with hydrogen, coal power plant cofiring with natural gas.|
||MULTI_FUELS = 0: Not part of set (default) |
||MULTI_FUELS = 1: Resources that can use fuel blending. |
|**Technical performance parameters**|
|Num\_Fuels |Number of fuels that a multi-fuel generator (MULTI_FUELS = 1) can use at the same time. The length of ['Fuel1', 'Fuel2', ...] should be equal to 'Num\_Fuels'. Each fuel will requires its corresponding heat rate, min cofire level, and max cofire level. |
|Fuel1 |Frist fuel needed for a mulit-fuel generator (MULTI_FUELS = 1). The names should match with the ones in the `Fuels_data.csv`. |
|Fuel2 |Second fuel needed for a mulit-fuel generator (MULTI_FUELS = 1). The names should match with the ones in the `Fuels_data.csv`. |
|Heat1\_Rate\_MMBTU\_per\_MWh |Heat rate of a multi-fuel generator (MULTI_FUELS = 1) for Fuel1. |
|Heat2\_Rate\_MMBTU\_per\_MWh |Heat rate of a multi-fuel generator (MULTI_FUELS = 1) for Fuel2. |
|Fuel1\_Min\_Cofire\_Level |The minimum blendng level of 'Fuel1' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the normal generation process. |
|Fuel1\_Min\_Cofire_Level\_Start |The minimum blendng level of 'Fuel1' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the start-up process. |
|Fuel1\_Max\_Cofire\_Level |The maximum blendng level of 'Fuel1' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the normal generation process. |
|Fuel1\_Max\_Cofire_Level\_Start |The maximum blendng level of 'Fuel1' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the start-up process. |
|Fuel2\_Min\_Cofire\_Level |The minimum blendng level of 'Fuel2' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the normal generation process. |
|Fuel2\_Min\_Cofire_Level\_Start |The minimum blendng level of 'Fuel2' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the start-up process. |
|Fuel2\_Max\_Cofire\_Level |The maximum blendng level of 'Fuel2' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the normal generation process. |
|Fuel2\_Max\_Cofire_Level\_Start |The maximum blendng level of 'Fuel2' in total heat inputs of a mulit-fuel generator (MULTI_FUELS = 1) during the start-up process. |

## 3 Outputs

Expand Down
56 changes: 55 additions & 1 deletion src/load_inputs/load_resources_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,17 @@ function add_resources_to_input_data!(inputs::Dict, setup::Dict, case_path::Abst
# For now, the only resources eligible for UC are themal resources
inputs["COMMIT"] = inputs["THERM_COMMIT"]

# Set of CCS resources (optional set):
inputs["CCS"] = ids_with_positive(gen, co2_capture_fraction)

# Single-fuel resources
inputs["SINGLE_FUEL"] = ids_with_singlefuel(gen)
# Multi-fuel resources
inputs["MULTI_FUELS"] = ids_with_multifuels(gen)
if !isempty(inputs["MULTI_FUELS"]) # If there are any resources using multi fuels, read relevant data
load_multi_fuels_data!(inputs, gen, setup, case_path)
end

buildable = is_buildable(gen)
retirable = is_retirable(gen)

Expand Down Expand Up @@ -1060,7 +1071,11 @@ function add_resources_to_input_data!(inputs::Dict, setup::Dict, case_path::Abst

# Fuel
inputs["HAS_FUEL"] = ids_with_fuel(gen)

if !isempty(inputs["MULTI_FUELS"])
inputs["HAS_FUEL"] = union(inputs["HAS_FUEL"], inputs["MULTI_FUELS"])
sort!(inputs["HAS_FUEL"])
end

inputs["RESOURCES"] = gen
return nothing
end
Expand Down Expand Up @@ -1131,3 +1146,42 @@ function load_resources_data!(inputs::Dict, setup::Dict, case_path::AbstractStri
return nothing
end
end

@doc raw"""
load_multi_fuels_data!(inputs::Dict, setup::Dict, path::AbstractString)
Function for reading input parameters related to multi fuels
"""
function load_multi_fuels_data!(inputs::Dict, gen::Vector{<:AbstractResource}, setup::Dict, path::AbstractString)

inputs["NUM_FUELS"] = num_fuels.(gen) # Number of fuels that this resource can use
max_fuels = maximum(inputs["NUM_FUELS"])
inputs["FUEL_COLS"] = [ Symbol(string("Fuel",f)) for f in 1:max_fuels ]
fuel_types = [fuel_cols.(gen, tag=f) for f in 1:max_fuels]
heat_rates = [heat_rate_cols.(gen, tag=f) for f in 1:max_fuels]
max_cofire = [max_cofire_cols.(gen, tag=f) for f in 1:max_fuels]
min_cofire = [min_cofire_cols.(gen, tag=f) for f in 1:max_fuels]
max_cofire_start = [max_cofire_start_cols.(gen, tag=f) for f in 1:max_fuels]
min_cofire_start = [min_cofire_start_cols.(gen, tag=f) for f in 1:max_fuels]
inputs["HEAT_RATES"] = heat_rates
inputs["MAX_COFIRE"] = max_cofire
inputs["MIN_COFIRE"] = min_cofire
inputs["MAX_COFIRE_START"] = max_cofire_start
inputs["MIN_COFIRE_START"] = min_cofire_start
inputs["FUEL_TYPES"] = fuel_types
inputs["MAX_NUM_FUELS"] = max_fuels
inputs["MAX_NUM_FUELS"] = max_fuels

# check whether non-zero heat rates are used for resources that only use a single fuel
for f in 1:max_fuels
for hr in heat_rates[f][inputs["SINGLE_FUEL"]]
if hr > 0
error("Heat rates for multi fuels must be zero when only one fuel is used")
end
end
end
# do not allow the multi-fuel option when piece-wise heat rates are used
if haskey(inputs, "THERM_COMMIT_PWFU") && !isempty(inputs["THERM_COMMIT_PWFU"])
error("Multi-fuel option is not available when piece-wise heat rates are used. Please remove multi fuels to avoid this error.")
end
end
Loading

0 comments on commit 744896c

Please sign in to comment.