Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a feature to enable thermal power plants to burn multiple fuels #586

Merged
merged 61 commits into from
Feb 20, 2024

Conversation

qluo0320github
Copy link
Collaborator

@qluo0320github qluo0320github commented Nov 27, 2023

Description

This PR adds the feature that enables thermal power plants to burn multiple fuels.

Cofiring relatively cleaner energy is one of the carbon mitigation options, such cofiring H2 in natural gas turbines. This feature enables thermal generators ("THERM" in GenX) to burn multiple fuels at the same time and also allows the user to specify the cofiring 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 cofiring.

Variables in Generators_data.csv required to use this feature: "MULTI_FUELS", "Num_Fuels", "Fuel1", "Heat_Rate1_MMBTU_per_MWh", "Fuel1_Min_Cofire_Level", "Fuel1_Min_Cofire_Level_Start", , "Fuel1_Max_Cofire_Level", "Fuel1_Max_Cofire_Level_Start", "Fuel2", "Heat_Rate2_MMBTU_per_MWh", "Fuel2_Min_Cofire_Level", "Fuel2_Min_Cofire_Level_Start", , "Fuel2_Max_Cofire_Level", "Fuel2_Max_Cofire_Level_Start" (assuming "MULTI_FUELS" = 1 and "Num_Fuels" = 2).

What type of PR is this? (check all applicable)

  • Feature

Checklist

  • Code changes are sufficiently documented; i.e. new functions contain docstrings and .md files under /docs/src have been updated if necessary.
  • The latest changes on the target branch have been incorporated, so that any conflicts are taken care of before merging. This can be accomplished either by merging in the target branch (e.g. git merge develop) or by rebasing on top of the target branch (e.g. git rebase develop). Please do not hesitate to reach out to the GenX development team if you need help with this.
  • Code has been tested to ensure all functionality works as intended.
  • CHANGELOG.md has been updated (if this is a 'notable' change).
  • I consent to the release of this PR's code under the GNU General Public license.

How this can be tested

An example case is add to /Example_Systems/SmallNewEngland/ThreeZones_Multi_Fuel.
For any other cases without multi fuels, this feature should not change the results.

Post-approval checklist for GenX core developers

After the PR is approved

  • Check that the latest changes on the target branch are incorporated, either via merge or rebase
  • Remember to squash and merge if incorporating into develop

fc4uk and others added 30 commits May 16, 2023 18:11
1.	In load_fuels_data.jl, there is no need to scale fuel_co2 as it will cause scaling issues when the parameter scale is on.
2.	In load_generator_data.jl, remove the start up fuel costs as all the costs associated with fuel consumption will be accounted in fuel.jl.
3.	In discharge.jl, remove the fuel costs as they will be separately accounted in fuel.jl
4.	In Generate_model.jl, remove emissions!(EP, inputs) as emissions.jl will be replaced by CO2.jl. include fuel.jl.
5.	Fuel.jl is a module that tracks the consumption (MMBTU or billion BTU scaled) and costs ($ or million $) of fuels used in generators (startup fuels included as well). It also includes a piecewise fuel consumption option if you want to represent the fuel consumption during different load factor.
6.	CO2.jl is an updated version of emissios.jl, and we don’t need emissions.jl when we have co2.jl. Also added BECCS options in CO2.jl, line 29 and line 39. Basically, when BECCS plants are included, “Generator_data.csv” should have a column named “BECCS”, and the values under “BECCS” column should be 1 for BECCS facilities and 0 for non-BECCS facilities. The amount of CO2 captured and stored for BECCS generators are accounted in the same way as other thermal generators, but its corresponding emissions should be negative.
7.	Modified write_costs such that fuel costs are appropriately included.
8.	Include write_co2.jl and write_fuel_consumption.jl, which give co2 emissions and fuel consumption for each generator.

Co-Authored-By: Qingyu Xu <xuqingyu0610@gmail.com>
accidently deleted eEmissionsByZone... added them in this version.

Co-Authored-By: Qingyu Xu <xuqingyu0610@gmail.com>
1. Reduce the number of variables by removing P1 and P2
2. Add a placeholder for possible different heat rates when cofiring
3. Change column name "Fuel1" back to "Fuel"
…a loading script to use defaults if second fuel is not in input file
Add constraints related to EPA new rules: capacity factor, emission rate, ...
Add varialbes "THERM_DUAL" and "THERM_SING"
Apply maximum co-fire level constraints to THERM_DUAL

\
allow users to specify "Fuel1", "Fuel2", ..., "FuelN"
enable resources to use multi fuels for both startup and operations
remove codes related to "fuel2"
create a new example case "ThreeZones_Multi_Fuel"
include multiple fuels in write_fuel_consumption
separate consumption and costs of fuels during startup and normal operations when writing outputs
add descriptions and math for multi fuels
make heat rates 0 for non-thermal resources
add conditional evaluation to fuel.jl and write_fueL_consumption so that the model can run when multi_fuel does not exist.
Separate startup and generation fuels when writing outputs
Add blending level constraints during startup processes and corresponding new columns in Generators_data.jl
Fix bugs for blending constraints
1. add fuel cost for multi-fuel resources (ePlantCFuelOut_multi, ePlantCFuelOut_multi_start);
2. make eCFuel_out only consider fuel costs during generation;
3. change *_startup to _start to be consistent with other files;
4. delete the column for start fuel costs when writing costs and combine it with generation fuel cost
5. add error message when both "PieceWiseHeatRate" and "MULTI_FUELS" exist;
6. fix some typos when calculating fuel costs.
1. enable RETROFIT by commenting out the error message in load_generators_data.jl
2. add the variable "MULTI_FUELS" to indicate whether a resource uses multi fuels when writing fuel consumption
3. bug fix
1. Fix eFuelConsumption and fuel costs for MULTI_FUELS resources;
2. Fix bugs in writing out retrofitted capacity;
3. Disable writing some CO2 emission outputs when only unit-level CO2 emission constraints are used
1. Remove constraints on capacity factors
2. Remove constraints on unit-level emission rates
3. Change a column name in Fuel_cost_plant.csv (from "AnnualSum" to "AnnualSumCosts)
4. Remove EPA example system
Add an optional formulation for scheduled maintenance. Plants with this formulation active (so far, limited to thermal-commit plants, THERM=1) need to undergo a certain number of contiguous hours of maintenance every y >= 1 years. During this time they produce no power.

This may be particularly useful in modeling fission plants, which need roughly 4 weeks of maintenance every 18 or 24 months. (Here, 18 would need to be rounded up to 24, as only maintenance cycles which are an integer number of years work with this formulation.)
Change the New_Build column to accept only {0, 1}; add a new required column Can_Retire which handles that feature separately (also {0, 1}).

The change is backward-compatible, with a warning to update.
---------

Co-authored-by: Gabe Mantegna <gabe.mantegna@princeton.edu>
Fixed computation of cumulative minimum retirements in multistage code. Previously, minimum capacity retirement parameters were not added up and this resulted in the minimum retirement constraints enforcing wrong lower bounds (#542)
Copy link
Collaborator

@lbonaldo lbonaldo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @qluo0320github; this is an excellent contribution! I added a few comments with suggestions for minor changes, but overall, this PR is already in great shape.

inputs_gen["RETRO"] = gen_in[gen_in.RETRO.==1,:R_ID]
# Disable Retrofit while it's under development
if !(isempty(inputs_gen["RETRO"]))
error("The Retrofits feature, which is activated by nonzero data in a 'RETRO' column in Generators_data.csv, is under development and is not ready for public use. Disable this message to enable this *experimental* feature.")
end

# Set of multi-fuel resources
if "MULTI_FUELS" ∉ names(gen_in)
gen_in[!, "MULTI_FUELS"] = zero(gen_in[!, "R_ID"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use zeros(G) to create a vector of zero elements of length G (it should be faster because it doesn't read the data frame)

Suggested change
gen_in[!, "MULTI_FUELS"] = zero(gen_in[!, "R_ID"])
gen_in[!, "MULTI_FUELS"] = zeros(G)

@@ -219,7 +223,7 @@ function load_generators_data!(setup::Dict, path::AbstractString, inputs_gen::Di
end

load_vre_stor_data!(inputs_gen, setup, path)

load_multi_fuels_data!(inputs_gen, setup, path)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest defining here the two entries inputs_gen["SINGLE_FUEL"] and inputs_gen["MULTI_FUELS"] instead of inside the load_multi_fuels_data!, and adding the function call inside a if !isempty(inputs_gen["MULTI_FUELS"]) end block. Something like:

# Single-fuel resources
inputs_gen["SINGLE_FUEL"] = gen_in[gen_in.MULTI_FUELS.!=1,:R_ID]
# Multi-fuel resources
inputs_gen["MULTI_FUELS"] = gen_in[gen_in.MULTI_FUELS.==1,:R_ID]
if !isempty(inputs_gen["MULTI_FUELS"]) # If there are any resources using multi fuels, read relevant data
    load_multi_fuels_data!(inputs_gen, setup, path)
end

inputs_gen["NUM_FUELS"] = gen_in[!,:Num_Fuels] # Number of fuels that this resource can use
max_fuels = maximum(inputs_gen["NUM_FUELS"])
fuel_cols = [ Symbol(string("Fuel",i)) for i in 1:max_fuels ]
heat_rate_cols = [ Symbol(string("Heat_Rate",i, "_MMBTU_per_MWh")) for i in 1:max_fuels ]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for f in 1:max_fuels might be more readable than for i in 1:max_fuels

Comment on lines 262 to 263
else
error("Multi-fuel option is not available when piece-wise heat rates are used. Please remove multi fuels to avoid this error.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'd also suggest moving this check in load_generators_data (for example, inside process_piecewisefuelusage! or load_multi_fuels_data!)

@@ -169,7 +178,7 @@ function write_net_revenue(path::AbstractString, inputs::Dict, setup::Dict, EP::
end

dfNetRevenue.Revenue = dfNetRevenue.EnergyRevenue .+ dfNetRevenue.SubsidyRevenue .+ dfNetRevenue.ReserveMarginRevenue .+ dfNetRevenue.ESRRevenue .+ dfNetRevenue.RegSubsidyRevenue
dfNetRevenue.Cost = dfNetRevenue.Inv_cost_MW .+ dfNetRevenue.Inv_cost_MWh .+ dfNetRevenue.Fixed_OM_cost_MW .+ dfNetRevenue.Fixed_OM_cost_MWh .+ dfNetRevenue.Var_OM_cost_out .+ dfNetRevenue.Var_OM_cost_in .+ dfNetRevenue.Fuel_cost .+ dfNetRevenue.Charge_cost .+ dfNetRevenue.EmissionsCost .+ dfNetRevenue.StartCost
dfNetRevenue.Cost = dfNetRevenue.Inv_cost_MW .+ dfNetRevenue.Inv_cost_MWh .+ dfNetRevenue.Fixed_OM_cost_MW .+ dfNetRevenue.Fixed_OM_cost_MWh .+ dfNetRevenue.Var_OM_cost_out .+ dfNetRevenue.Var_OM_cost_in .+ dfNetRevenue.Fuel_cost .+ dfNetRevenue.Charge_cost .+ dfNetRevenue.EmissionsCost .+ dfNetRevenue.StartCost +dfNetRevenue.CO2SequestrationCost
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formally, it's identical, but for consistency, you could do the following:

Suggested change
dfNetRevenue.Cost = dfNetRevenue.Inv_cost_MW .+ dfNetRevenue.Inv_cost_MWh .+ dfNetRevenue.Fixed_OM_cost_MW .+ dfNetRevenue.Fixed_OM_cost_MWh .+ dfNetRevenue.Var_OM_cost_out .+ dfNetRevenue.Var_OM_cost_in .+ dfNetRevenue.Fuel_cost .+ dfNetRevenue.Charge_cost .+ dfNetRevenue.EmissionsCost .+ dfNetRevenue.StartCost +dfNetRevenue.CO2SequestrationCost
dfNetRevenue.Cost = dfNetRevenue.Inv_cost_MW .+ dfNetRevenue.Inv_cost_MWh .+ dfNetRevenue.Fixed_OM_cost_MW .+ dfNetRevenue.Fixed_OM_cost_MWh .+ dfNetRevenue.Var_OM_cost_out .+ dfNetRevenue.Var_OM_cost_in .+ dfNetRevenue.Fuel_cost .+ dfNetRevenue.Charge_cost .+ dfNetRevenue.EmissionsCost .+ dfNetRevenue.StartCost .+ dfNetRevenue.CO2SequestrationCost

@sambuddhac
Copy link
Collaborator

Needs to check the write time performance once again by allocating more memory. @qluo0320github @filippopecci

@qluo0320github
Copy link
Collaborator Author

I ran multiple times with different memory and it did not change writing time significantly. @sambuddhac

@lbonaldo lbonaldo self-assigned this Feb 20, 2024
@lbonaldo lbonaldo merged commit 744896c into develop Feb 20, 2024
9 checks passed
@lbonaldo lbonaldo deleted the multi_fuels branch April 29, 2024 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants