EnergyInnovation / eps-us

Energy Policy Simulator - United States
GNU General Public License v3.0
22 stars 7 forks source link

Move to hourly electricity dispatch #232

Closed robbieorvis closed 3 months ago

robbieorvis commented 2 years ago

This builds on the discussion of other electricity sector improvements in issues #219 and #106 and logs as an issue conversations we've been having already about electricity dispatch.

We should consider moving to an hourly dispatch model to improve dispatch and reliability modeling. I completed a proof of concept of this approach in #106 that can be easily implemented and does not appear to result in a significant increase in runtime.

In evaluating the California EPS it has become very obvious that this improvement is extremely important to evaluate scenarios of deep electricity sector decarbonization with any level of confidence. This is because solar is the dominant low carbon electricity source dispatched, but when evaluating the hourly load requirements and solar availability, it is clear that the current approach of looking at peak annual seasonal demand and resource availability does not capture the reliability limitations of a power system. For example, in the CA model, solar has a pretty high capacity factor during peak hours, but it a zero capacity factor in later hours of the day when demand is high but not at peak levels. Using an annual approach, the model assumes that all demand can be met and even that peak is satisfied. But the real issue is the requirement to meet net demand after removing variable generation. To get that right, we need some representation of hourly load and resource availability. Because the implementation of an hourly modeling approach in #106 proved viable and fast, I just suggest we use this approach.

In estimating reliability, the model could look at load in a year and existing resources and do a modeling pass to see if there is a shortfall in any given hour, after applying the reserve margin. This is likely to result in need for additional resources to cover hours where there is a large net load requirement (net load = demand - variable renewable generation). In California, this is often in the evening hours, which is what I anticipate the modeling will show.

Moving to hourly dispatch will do a much better job representing system constraints and resource availability, improving our ability to model reliability capacity requirements while also allowing us to do economic capacity expansion as outlined in #106. It does likely require us to move back to ALLOCATE AVAILABLE, but as discussed elsewhere, this is required anyway given the limitation of the logit functions to model physical constraints.

mkmahajan commented 1 year ago

I plan to continue work on the retirements code and also review Jeff's work on the RPS this week, since Robbie let me know he's not planning on structural work this week. So I will recreate my retirements work on top of the latest commit in #232. I think I will be the only one working on the .mdl file before Friday, but please let me know if anyone is planning to make edits during that time.

mkmahajan commented 1 year ago

Ahead of today's model dev session, I'm pushing some changes I've worked on this week. These include:

jrissman commented 1 year ago

Today's work focused on improving the use and calculation of capacity factors, which are now a lynchpin of the electricity sector's behavior, since we've we've removed "BECF BAU Expected Capacity Factors" and are calculating input capacity factors from the hourly data, and we've switched over to using time-delayed "Achieved" capacity factors from prior timesteps in several parts of the calculations. In the morning, we finished the work to support technological improvements to capacity factors in future years, affecting hourly, new plant, and weighted average capacity factors. We also fixed a minor issue with nuclear capacity factor not being as "expected" despite being defined in input data and set to always dispatch. In the afternoon, we improved the dispatch calculations by making a separate dispatch step for zero- and negative-cost resources, rather than trying to stuff them into the optimized least-cost allocation, which did not properly handle hours in which there were sufficient zero- and negative-cost resources to fully meet demand.

All of this was necessary groundwork to help us understand and fix our problems with capacity factors, but the heart of the problem wasn't addressed today, and I think it is the top priority item in this coming week. You can most easily visualize one aspect of the problem by looking at the graph for the variable "Last Year Smoothed Achieved Capacity Factors," because year 2020 in this graph is based on input data, and year 2021 is the first year with our calculated data.

LastYearSmoothedAchievedCapacityFactors

You can see that our calculated values in 2021 are dramatically different from the real-world values in 2020, even though the electricity system has barely changed between 2020 and 2021 (as there hasn't been enough time for many plants to be built or to retire). Differences in electricity demand aren't that large between these two years either, and in any case, even an increase in electricity demand in 2021 would not explain the capacity factors of some plant types (like biomass) suddenly plunging toward zero. We should see why our achieved capacity factors are so different from real-world data and try to better align them.

Also (and this may be a separate matter), we should ensure the capacity factors we use to decide what plant types to build are not overly sensitive to year-over-year changes (such as a one-year price jump in natural gas, which utilities would know is likely transient). Rather, I think it would be better if the expected capacity factors reflect changes in the electricity system as a whole. For instance, as the share of solar on the grid gets high, the capacity factor the model expects for newly built solar should gradually diminish. It might at least be worth an attempt to calculate "expected" capacity factors from the total potential output of each plant type that exists, and the total demand, rather than based on time-delayed achieved capacity factors, as I think this may give smoother and more realistic behavior while still reflecting the changing composition of the electricity grid and changing levels of electricity demand. (That said, if our achieved capacity factors are perfect, then time-delayed achieved capacity factors might start to seem like a fine solution. So I'd suggest working on improving the achieved capacity factors first, then reassessing whether we need to calculate "expected" capacity factors.)

If we can get those two things: good alignment of our achieved capacity factors in the first calculated year with real-world data from the prior year, and a way to have smoother behavior for the capacity factors used to determine what plant types to build, I think the outputs are likely to start looking pretty good, and then we will be in a better position to evaluate and finish the CES and other policies, and to work on the demand-shifting technologies.

jrissman commented 1 year ago

Today we worked on understanding and fixing problems in the achieved capacity factors. Tracing issues with various plant types led us to the Start Year Capacity and BAU Heat Rate input data files. It turned out these files were not in sync with each other (CHP plants were being excluded from one but not the other), and there were quite a few other formula errors that were causing incorrect numerical results. The solution was to consolidate SYC and BHR into a single Excel file that outputs two .csv files, so that these two .csv files can be automatically kept in-sync with each other. The new combined Excel file, BHRaSYC, aims to be as streamlined and understandable as possible. There were a number of hurdles to surmount, mostly related to the particulars of the two EIA data tables (Form 923 and Form 860) and matching plants between them. These can be quite detailed, and I won't get into it here, but everything is documented inside the BHRaSYC Excel file. I've tried to construct it in such a way that one doesn't need to understand these details simply to update to a later-year-version of Forms 923 and 860, since a copy-and-paste should work (if EIA hasn't changed the columns, fuel type codes, or prime mover codes in the new year's forms).

Fixing the errors and inconsistencies in SYC and BHR made a significant difference in the capacity factors across several plant types, as well as what the model chooses to build, retire, and to dispatch. For instance, coal is now retiring out in the BAU case, which it was not doing before correcting these data errors. One can't always separate structural work from input data work, and it is sometimes necessary to fix input data issues before one can work effectively on structural matters.

One necessary enhancement to that file is that the filter settings (which include or exclude CHP plants, and include or exclude non-utility-sector plants) can now be set separately for each plant type. This was necessary for biomass plants because a majority of biomass capacity is not in the utility sector, including the most efficient plants, and there is no way to make sense of the capacity factors reported for biomass (from the hourly capacity factor data) without factoring in these important biomass plants. Biomass is currently the only plant type including non-utility plants in its heat rate and capacity figures. (The filter settings actually make little difference for any plant type other than biomass.) This has mostly fixed the problem with biomass capacity factor, and adjusting the Weibull curve coefficients a and b to make the biomass curve shape less spiky might help further.

Note that we were testing using constant natural gas prices (repeating 2030 values for years 2021-2029) at Megan's suggestion, but I didn't push these natural gas price changes to the server, since they were intended to be temporary changes for testing purposes. After we get the capacity factors looking good with the flat NG pricing, we should restore normal NG pricing and see how it performs. We may need a mechanism to prevent utilities from over-reacting to transient fuel price spikes in their construction and retirement decisions. I'm thinking of using something like a rolling 4-year average, which will gradually incorporate fuel price changes into decision-making.

More work remains to be done on capacity factors and dispatch, but today got some necessary groundwork out of the way.

mkmahajan commented 1 year ago

I had some time to work on the electricity sector this week and made a few updates, summarized below.

  1. I had a chance to chat with Robbie about the RAF Regional Availability Factor. This is primarily needed for coal plants, and right now is only using values other than 1 for hard coal and lignite. The reason why this was added in the first place is because coal plants are very regionally specific in the US. For example, there are no coal plants in California and effectively none in much of New England, two regions that make up a large portion of electricity demand. So the RAF value helps account for the fact that US coal capacity is not regionally available to large swaths of the country (more so than other plant types). The maximum hourly capacity factor for hard coal is about 80%, which seems like a good value to apply to RAF to approximate the most coal that could be available to dispatch in a given hour.
  2. I also applied the Multiplier for Natural Gas Peaker Fuel Costs that I had added to steam turbines, which got rid of most of the discrepancy between our start year data and what is dispatched.
  3. I realized that our Weibull parameters also use Form 923. I thought combining heat rates, start year capacity, and the Weibull curves would be a very large file, so I took Jeff's new system for filtering plants and added it to the EDWP file. I can always go back and combine files. This didn't change much except for a small change to combined cycle and biomass plants.
  4. I found an error in the Estimating Capacity Prices section, where the additional market revenue was using a variable that had been updated from units capacity to units output, when it still needed that value in terms of unit capacity. The model hadn't been building very much capacity because of that error. The fix helps bring down some of the high capacity factors we were seeing in later years of the model run.
  5. I cleaned up several hard coded variables that we wanted as input data files and split the load factors into a separate file from the hourly capacity factors.
  6. I added in the rest of the retirement Weibull parameters (I had only filled out a few test plant types before). I do still need to go back and make sure these use the new plant filtering methodology though.

After these changes, the Last Year Smoothed Achieved Capacity Factors are looking better, but not perfect. image

Coal is still somewhat overdispatching. But Robbie and I were also discussing that our NREL source data shows capacity factors that don't neatly match up to what EIA reports. And that if the ReEDS model can't even align nicely, then maybe we're okay to be a few percent off in either direction relative to the source data. Also note that this graph is still showing the smoothed natural gas prices through 2030 that we were using last Friday.

Biomass is still the outlier with its jagged trajectory. However, I found that's due to the heat rates by vintage. There's a very large range in historical heat rates, which greatly influence the weighted average heat rate since there is so little biomass capacity. Here's our graph of weighted average biomass heat rates: image

Finally, the fix number 4 above means that there are very little retirements now, because we were effectively missing a source of revenue before. I'll need to revisit the retirements section in light of the higher capacity prices.

robbieorvis commented 1 year ago

Thanks, Megan. Sounds like we are making more good progress!

A few follow-ups:

  1. There's two other reasons for the regional availability factor in addition to location of coal: fuel price variability and supply constraints. This is broadly true of all fuel sources but really notable for coal, where some regions have coal prices that are 3x the national average. The second is fuel supply constraints. One thing we observed last year is that even when the price dynamics suggest that coal should be dispatching at a much higher capacity factor, it's coal supply is limited, and the power plants will optimize their generation throughout the year to maximize profitability, even if it means under dispatching at times when it is marginally profitable. This is why we only saw a small increase in coal dispatching in 2022, despite the fact that gas prices were much higher. Because we have no way to handle these two dynamics, plus the geographic constraint Megan described, we need something like the RAF to handle this (unless we want to build something much more complex or include some type of price adder).
  2. There's another factor that is worth mentioning at least which is the concept of reliability unit commitment. In the real world, plants that might not be economic in hours 1-3 but are economic and necessary for reliability in hour 4 will be turned on and run at their minimums in hours 1-3 so they are available when needed in hour 4. This concept is called reliability unit commitment, because a power plant is committed to dispatching in order to be available for reliability. When this happens in real markets, those plants are made whole in the hours they otherwise would lose money. Because natural gas plants are much more rampable than coal plants, this has the effect of pushing more gas onto the system even in hours when it may not be economic. We should not endeavor to attempt this in the EPS for a variety of reasons, but flagging that it is one of the real world reasons that plants might have different capacity factors in our model than in others/real world data.
  3. I'll just reiterate Megan's point that while we should try get close on capacity factors, especially for the most significant plant types, we are not going to be able to perfectly replicate real world results nor other models (unless we include some more direct calibrating mechanisms, which I don't think we want). Our goal should be to get close enough and to have the model mimic real world behavior as best as possible, but we'll have to sacrifice some accuracy to historical data most likely.

Last comment: I wouldn't put stock in the capacity factors after the first few years until we are sure we think dispatch, retirements, and additions are working correctly, because it's clear the model is underbuilding, which is causing capacity factors for basically every type of resource to increase over time. I would just focus on the first few years to get dispatch working correctly, then move onto retirements and additions.

robbieorvis commented 1 year ago

Total aside, here is EPA’a IPM model documentation.

https://www.epa.gov/system/files/documents/2023-03/EPA%20Platform%20v6%20Post-IRA%202022%20Reference%20Case.pdf [preview.png] EPA Platform v6 Post-IRA 2022 Reference Casehttps://www.epa.gov/system/files/documents/2023-03/EPA%20Platform%20v6%20Post-IRA%202022%20Reference%20Case.pdf PDF Document · 7.2 MBhttps://www.epa.gov/system/files/documents/2023-03/EPA%20Platform%20v6%20Post-IRA%202022%20Reference%20Case.pdf

mkmahajan commented 1 year ago

In one of his earlier write-ups, Robbie had asked to go back to using more granular electricity load factors by end use rather than by sector (like we use in the elec/ELF file in EPS 3.4.5). I've updated our SHELF file to give hourly load factors for each building end use for both residential and commercial buildings, each vehicle type in transportation, and then industry, district heat/hydrogen, and geoengineering at the sector level. I've also confirmed that even after this edit, multiplying the variable Total Electricity Demand by Hour before TnD Losses by Days per Electricity Timeslice results in the exact sum of all the annual sector-level electricity demand totals.

jrissman commented 1 year ago

Vensim 9.0.1 generates a bunch of incorrect error messages when running the current version of the model (as of the latest commit). Vensim 9.4.0 generates no error messages when running the exact same commit. It appears Vensim has a bug in 9.0.1 that was fixed by 9.4.0 that causes spurious error messages with our model. I think we need to update the required version of Vensim on the first sheet, which currently just says Vensim 8 or later is required.

Unfortunately, in anything later than Vensim 9.2.4, the Tools > Options dialogue is broken and doesn't appear when using the old sketch view. The same error message that comes up when you try to access the Options dialogue also appears when you close the model, with no indication of what is causing the error. This makes the model seem broken. Switching to the new sketch view allows the Options menu to be used.

Vensim 9.2.4 appears to be in a sweet spot where it's not generating the spurious runtime errors, but it is before Ventana broke the Tools > Options dialogue in the old sketch view. I'm going to update the first sheet of the model, where it currently says "Vensim 8 or later (64-bit) is required" to instead say "Old sketch view: Vensim 9.2.4 is recommended. New sketch view: Vensim 9.4.0 or later is recommended."

jrissman commented 1 year ago

Okay, it's been a really long day looking at this. Here's what I found.

New_Structure

Right now, the multiplier is not subscripted by plant type and is set to 2.

Here's a screenshot of our BAU scenario electricity capacity before adding the multiplier:

Before

Here's a screenshot of our BAU scenario electricity capacity after adding the multiplier:

WeibullMeanDoubled

It may not be perfect, but it seems like a big improvement. It fixes the main problem of too much of the total value being funneled through the capacity payments and too little being funneled through the energy market payments. There were similar improvements to the electricity generation graph.

One thing to note is that the results are quite sensitive to the multiplier value. We'll probably want to subscript it by plant type, and you'll probably find it important to tinker with this multiplier when making versions of the model for other geographies.

@robbieorvis and @mkmahajan, if you have time, can you take a look at this fix (given the reasoning above) and let me know what you think? You can just enter SyntheSim and set the multiplier to 1 to compare to the results it had before.

robbieorvis commented 1 year ago

Thanks, Jeff. I will take a look today.

robbieorvis commented 1 year ago

Okay a few thoughts here during the time I was able to review this morning:

  1. The number one biggest issue at the moment is that our hourly electricity demand is far too high. Here is a report from EIA saying the peak in the last few years was 720 GW: https://www.eia.gov/todayinenergy/detail.php?id=49216. NREL's ReEDS model also confirms ~700 GW demand at peak. In the model now, we are getting to 820 GW, a ~100 GW gap, which probably helps explain why at least in the early years the model wants to build so many peakers (eg 60+ GW/year). I think we need to double check our hourly load data and equations. I seem to recall values being closer to the high 600s or low 700s prior to recently made edits. I believe that one of the reasons for this is that in SHELF we are using data form 2040. That is supposed to reflect a midpoint in the model and the values are only being used as multiplier, but I think that updating to use 2020 data is likely to significantly lower our peak demand. As a result of demand that is too high, the model is building all those peakers in the first few years, with weird impacts.
  2. Another issue is we are only allowing gas peakers to be built for reliability, but this is not really how things function, and it has implications in the model. Mainly, this configuration is important because in determining the capacity market price, because we are only tying the required market revenue to peakers, not to combined cycle plants or other lower cost options. But for example, the revenue requirement for a peaker plant net of energy market revenues is probably about 70% higher than a combined cycle plant. This drives up the capacity market prices a lot and is one of the reasons too much revenue is flowing to the capacity market and too little to the energy market prices.
  3. Energy market revenues without the adjustment you made actually look correct. They are too high with the adjustment. We should revisit whether or not the adjustment is necessary after checking on 1 and 2 above.
  4. Changing 2 above and reverting to the unaltered energy market prices results in a much more realistic capacity buildout, even without correcting for the issue in 1) above. The model is building more gas than we'd expect, but that's likely due to the issues in 1 above.
jrissman commented 1 year ago

In commit f7ec08f, I added LFHVM Load Factor Hourly Variance Multiplier. It adjusts SHELF values by reducing or increasing hourly variance without changing overall demand. I've calibrated it such that our peak hour demand in the first modeled year matches the value EIA gave here (720 GW).

LFHVM is subscripted by sector and by timeslice, so it gives you fine-tuned control over when and in what sectors to increase or decrease variance. I've done this in the simplest possible way by setting all timeslices and sectors to use the same multiplier - the multiplier that gives us the right peak demand. @robbieorvis or @mkmahajan , if you want to, you can always add more nuance to the LFHVM data file by entering different values for different sectors and timeslices. That would be a data-only edit. I don't think it would prove to be that useful for getting the electricity sector working well, so I'm not going to attempt to add sector- or timeslice-specific nuance to LFVHM settings personally.

I think LFVHM is going to be crucial when adapting the EPS to other geographies. Some geographies won't have their own SHELF data sources and will need to adapt the U.S. one using LFHVM. But even in regions that do have their own SHELF data sources, the odds are good that the data will not be perfect and may need some adjustment using LFHVM.

This, I believe, will be the cleanest and most geographically adaptable way of addressing Robbie's comment 1 in the post above.

I ran out of time before I could address Robbie's comment 2 (as I ended up needing to spend a lot of today on work related to hiring). But here's a basic workplan for the next steps:

On a different topic, I think there is no reason to remove the end uses from SHELF because if you don't want them or don't have data for them, you can just repeat the sector-wide values for SHELF in each end use for that sector, and the effect would be the same as if we had removed the end uses entirely.

If we can get the items above working, there won't be too much left to do. After that point, some items on the to do list are:

I'm not sure if it's really possible to do it all by end of next week. Maybe if everything works right on the first try! :-) But we'll get as far as we can. We can plan to merge #232 in any event to unblock the model so the team can work on other things and make data updates while I'm out the week after next.

robbieorvis commented 1 year ago

Okay, I tested the fix coupled with reverting the price multiplier to 1 and allowing natural gas combined cycle units to be built for reliability, The results look fairly reasonable as a moving forward point. There are a few things I notice immediately though:

  1. We probably need some start year data values for the energy and capacity market revenues. The structure of the part that builds for profitability takes the min of the previous three years and since the start year is currently zero, that results in no capacity being built for cost-effectiveness for the first few years.
  2. A contributing factors is some weirdness in the first year of capacity market values, which are much, much smaller than subsequent years (and subsequent years seem to be more or less the same order of magnitude as one another. I'm not sure there's anything structurally here but it may be the case that we want a smoothing function (like a three year minimum or just a smooth) for capacity payments also, because of how annual energy market revenues can affect annual capacity payments. I think the reason for the variation may be due to the variation of gas prices in the input data.

I also started exploring a few other things here:

jrissman commented 1 year ago

Thanks for testing this, Robbie. I appreciate it. I will work on the things you mention today.

Regarding the potential need for start-year energy and capacity market revenues so we can estimate cost-driven retirements (and construction):

The first simulated year or two are typically historical years. In branch 232, the first simulated year is 2020 (with 2019 as the "start year" in relevant input data variables). We'll probably advance the first simulated year to 2021 or 2022 with the AEO 2023 data update. I think we may not want any cost-driven retirements in the first few years, and instead, to enter actual, historical retirements in the BAU capacity retirement schedule. Similarly, in plants built for cost-effectiveness, the model will never build any during the first simulated year due to the lack of data on energy and capacity market revenues, but since it's a historical year, we can simply put in what was actually built that year into the BAU mandated construction schedule. Even if the retirements need to look one or two years into the future, EIA has data on announced, upcoming plant retirements, which I expect should be relatively complete for plants retiring in just 1 or 2 years. This ought to give more accuracy than estimating energy and capacity market revenues, then trying to have the model guess at what was retired or built - better to rely on real historical data when we have it! :-)

robbieorvis commented 1 year ago

Good points! Yes let's use historical data, but then we should be sure we don't advanced the start year beyond when we have historical data, i.e. we probably need at least three years of historical data to overcome this issue (at least for now) in the power sector.

My schedule today and tomorrow is light, so just ping me if you need any testing or to bounce ideas around.

jrissman commented 1 year ago

Okay. I'll see if I can reduce the number of historical years we need when we update the retirement calculations. We may not need to check whether plants are not cost-effective for three consecutive years if we're also imposing some other limit on retirements, such as only retiring plants that are older than their expected lifetime.

Great to hear you have some time over the next couple days. I'll definitely ping you for testing or discussion and will report in this thread on each piece I complete and commit.

jrissman commented 1 year ago

It appears than when you don't have an EPS.mdls file (the file that Vensim saves locally to indicate user-specific settings), it will assume that only the first element of each subscript is active. You can create an EPS.mdls file by turning on all the subscript elements and saving the model, but it is likely many users/partners won't know they need to do that, and if they are using Vensim Model Reader, they will be unable to do it because Model Reader can't save the model. So I think we need to disable the setting that stores user-specific settings separately from the EPS.mdl file itself. This means that stuff like the currently selected variable (the last one you clicked on) and the currently displayed sheet will also be saved, which is too bad, but I think it's better than the issue where only the first element of every subscript starts off active.

jrissman commented 1 year ago

Alright, based on your feedback, @robbieorvis, I implemented a new reliability-based dispatch system. You indicated:

We would need to add structure in to compute the hourly capacity factor during the hour the model is building to meet instead of just building to meet peak demand. This ensure the model will adjust prices based on the availability during the hour of net maximum new capacity required.

This is what the new structure does. First, here's a screenshot (click to enlarge), and I'll explain the approach right below.

New_Reliability_Construction_Code

There are essentially two steps to the approach. First, we construct a synthetic timeslice by taking the worst hour (in terms of net demand, calculated as total demand minus electricity output from existing plants and from plants built this timestep due to upstream mechanisms like cost-driven capacity additions, policy-mandated capacity additions, and the RPS). This gives us the worst hour 0, the worst hour 1, the worst hour 2, the worst hour 3, and so forth. We assemble these into a "constructed timeslice" that essentially represents the worst that each hour can throw at us.

We then adjust the cost per unit electricity output from each plant type to reflect that plant type's availability (capacity factor) in each hour. Since plant type capacity factor also varies by season, we identify which timeslice each "worst hour" came from and pull the unique capacity factor for that hour and that timeslice. This way, we aren't giving solar PV its capacity factor from the summer if the worst hour was in the winter, etc.

Then we do an allocation using the logit function for hour 0, meeting demand at least cost given the adjusted costs for output in hour 0. We store the value, go to hour 1, determine how much electricity the stuff we built for hour 0 will output in hour 1, and then do a new logit allocation to cover any unmet demand in hour 1. We update the cumulative total and loop through all the remaining hours, updating the electricity output each hour for each thing that's already been built based on the new hour's capacity factors.

The model currently likes to build solar PV and natural gas combined cycle. It doesn't like onshore wind because although onshore wind is cheap, it has such low capacity factors in the hours we care about that it is significantly more expensive than natural gas combined cycle in those hours. If we think this is not correct, we should review the hourly wind capacity factor data. We also could update the logit exponent ETLE if you think it should have increased or decreased sensitivity to price differences between plant types.

Here's a BAU capacity graph:

Capacity

Here's a BAU electricity output graph:

Output

Here's a graph of what the new code is building to meet reliability needs. It's pretty steady through most years, though 2021 is unusually high and 2022 is zero. This might not matter too much if we have good data for what gets built in historical years, as we don't need or expect the model to perform well in the first year or so.

New_Cap_Reliability

I've disabled the multiplier I added last week that increased the location parameter C on the Weibull dispatch curves.

I've enabled retirements but only based on the BAU schedule (which has EIA's announced planned retirements in it). Cost-based retirement is currently disconnected, while I focus on making sure capacity additions are looking good. This makes the retirements roughly similar to the EPS 3.4.x series, which relied only on EIA's planned retirements in its BAU case.

A few items to note:

Robbie and possibly Megan, would you be able to test the new structure and see if it addresses the concerns you had with the old structure? I've been trying to address the concerns you write about in this thread, but you have better vision than me into what needs to be tested and what would constitute a fully working approach.

robbieorvis commented 1 year ago

This is clever and definitely a big step in the right direction. I see and understand how this works. A few thoughts:

  1. The hour in which we start almost certainly affects what is built, but we might be able to get around that (more on that in a moment).
  2. The cumulative capacity we are building is likely too high, because some of what's built in another hour could satisfy a previous hour. For example, consider a scenario with two hours: 12 PM and 12 AM. Let's assume there are two power plant types, gas and solar. Assume the model wants to build 100% solar for 12 PM and 100% gas for 12 AM (because solar's capacity factor is zero) and the reliability needed capacity is 100 MW in each hour. The gas has to be built for reliability at 12 AM... solar cannot provide any amount of reliability then. But if the gas is being built at 12 AM because it's necessary, that negates the need for the solar at noon. We would be building twice the capacity needed (assuming equivalent capacity factors at 12 PM). This example shows both how we would over build and also how the starting hour is very important. If the model had started at 12 AM, then it wouldn't have overbuilt, because the 12 PM slot would have been satisfied by the 100 MW new gas. But I think there is a good away to deal with this... (see 3)
  3. To address this, I think we can do two passes of the reliability calculations. The first pass operates like it does now, but it determines the amount of dispatchable capacity that is needed to meet reliability (i.e. it does the current pass but just sums dispatchable capacity and ignores what the model produces for non-dispatchable capacity). We could add a Boolean to toggle which plants are dispatchable. Then we take a VMAX to find the maximum amount of dispatchable capacity that is required after the first pass. We add that capacity to our total and then do another pass, where we let the model then build just based on costs to meet reliability. This approach would ensure we have enough dispatchable capacity when/where it is needed to meet reliability but allow for the incremental reliability need to be met with a least cost set of resources. In the prior example, this would lead to no overbuilding and just 100 MW of gas. In a case where 12 AM only had 50 MW of peak need, it would lead to 50 MW of gas and 50 MW of solar. And so on... If we do two passes like this, I think we can get an optimized mix of resources that ensure sufficient dispatchable capacity.
  4. It's possible I'm not seeing how the above aligns with the cumulative approach you've taken. One option would be to start without a cumulative pass, figure out the dispatchable capacity required, then go to the cumulative loop pass.
  5. On which costs to use, you raise good points. One thing I'm noticing now is that out cost per unit new elec output suffers from some of the same issues we've identified elsewhere, in that we use a single year's fuel prices to project the levelized future cost per unit elec output. That isn't quite right since in reality a single year's fuel prices shouldn't dictate whether or not a 30 year asset gets built... This is a different issue, but it might be worth exploring some type of normalization of fuel aspect of cost per unit elec output. Generally speaking, the capacity structure is producing the types of results I would expect, with combined cycle and solar PV being the predominant sources. It might be that increasing the logit exponent gets at the issue of too many different types of plants being built (which is my current assessment, at least for the earlier years, but part of that is due to a couple years of high gas prices raising the cost per unit new elec output, i.e. the issue I just raised. In any case, at a minimum, starting with cost per unit new elec output adjusted for hourly bid capacity factors seems like a good starting point and seems to be producing reasonable numbers.
  6. We should double check with EIA how they are reporting electricity demand in the hourly electric grid monitor (maybe @Megan @.***> can lead on this) to see if it is inclusive of estimated losses. Though it doesn't seem like a big deal, 5% of load at 720 GW is 36 GW, so if we are building in an extra 30 GW of needed capacity, that could be why our earlier years are building so much (I will also flag that EIA notes in that memo that 2021 was an abnormally hot year, and for the most part peak demand seems to have held right around 700 GW for the previous 5 years).
  7. I'm trying to reconcile findings in the model on peak demand and resource availability with this data from NERC: https://www.nerc.com/pa/RAPA/ra/Reliability%20Assessments%20DL/NERC_SRA_2022.pdf. This shows that basically every balancing hour has plenty of reserve margin capacity, and yet the model doesn't appear to think that's the case at the moment. I'm not sure why we have this discrepancy, but we should look into it apart, from the structural modifications. One of the issues, I think is that we are using Total Electricity Demand by Hour before Demand Altering Technologies in the reliability calculations, but I think we should be using the version with demand altering technologies (acknowledging here this introduces a simultaneous loop issue we'd have to address). Grid planners account for the demand altering technologies, especially pumped hydro and demand response, when planning for reliability, so this is an important component to include. This knocks off 20-30 GW of capacity need during peak hours in the first few years of the model run.

Okay, that's it for now. The upshot is that I think we are pretty close with some tweaks to having a working reliability additions section! Thanks for the work on this.

jrissman commented 1 year ago

I'm just logging here that Robbie, Megan, and I had a meeting today. We tested reliability-based construction in the BAU case and in a building electrification case (which made some of the worst hours winter peak hours rather than summer peak hours). We saw the reliability-based construction switch over to building wind and more NG rather than solar once those hours became the worst. Robbie thought it was looking pretty good. It does start with midnight, not with noon, which avoids the problem Robbie mentioned about building too much solar. We updated the demand flowing into the calculation to be time-delayed net demand after demand-altering technologies (batteries, demand response, etc.) rather than non-time-delayed net demand before demand-altering technologies. This eliminated some spiky behavior in 2021 and 2022. Our conclusion was that the reliability-based construction is working well and doesn't need more work at this time. We can move on to other sections.

We also updated the capacity credit calculation to pick the peak hour based on net demand rather than absolute demand, but this made no meaningful difference in the capacity payment amount or its distribution by plant type, at least in the BAU case.

We also discussed some next steps, and I'll post about each of those steps in this thread after I implement the relevant elements.

mkmahajan commented 1 year ago

Since we discussed testing the electricity sector with IRA tax credits, I've committed an update to the fuels/BS file to add in the expected credit amounts.

The exception is that I haven't yet added the nuclear PTC, because we'll have to customize those values later. (The nuclear PTC operates on a sliding scale based on the profitability of each plant and is essentially designed to be just enough to keep plants from retiring. So we'll want to find the BAU credit needed to avoid nuclear retirements once we have the cost-based retirement piece finalized).

jrissman commented 1 year ago

Here's a document I made with historical U.S. capacity additions and retirements based on EIA Electric Power Annual data going back to 2014. I will use this to help judge the reasonableness of the additions and retirements in the BAU case in early years of the model run (but not the first year or two). I'm using Net Summer rather than Nameplate capacity to reflect the values used in the EPS input data.

2023-05-09 Historical U.S. Cap Additions and Retirements v01.xlsx

jrissman commented 1 year ago

I was looking into cost-based capacity construction today, but I think there are problems in the fuels/BS BAU Subsidies file. There are #REF errors on the "Inflation Reduction Act" sheet, and the formula for solar PV on the BS-BSpUECB blue tab doesn't appear to be carried across all the columns. It also gives me a data connection warning on opening the file, which may be behind the #REF errors.

Right now, the total build rate of most electricity sources is in line with recent historical averages, but onshore wind is too low. Right now, the CES credit value is being calculated in a provisional/temporary way that takes no account of when the resource is available, so wind gets the same $/MWh as solar PV, even though wind might be dispatching in more valuable hours. Should we be accounting for hourly availability at all when calculating the CES credit value? Or is it the same for every MWh of clean electricity?

The drop-off in cost-based onshore wind construction in 2038-2039 appears to be due to the cessation of some subsidies in the BS BAU Subsidies file. We should make sure this is still the case after fixing the #REF errors in that file. If so, it may be a correct result, i.e., what would really happen if those subsidies are allowed to expire in 2038-2039. This is a different issue from why onshore wind construction appears to be too low in early years, when the subsidies are in effect.

I put in a variable that lets us adjust the share of costs that must be covered to be deemed profitable, though it's not clear to me yet that we need it to be set to anything other than 1.

I updated the code that checks for profitability in three consecutive years to check fewer years only at the very start of the model run, when fewer than three years of data exist. In this situation, it's more accurate to use fewer years to estimate a result than to assume zero profitability. In practice, this leads to smooth and correct-looking cost-based deployment starting in 2021 rather than in 2023, giving us two additional years of endogenous behavior.

There might not be a lot to do in this section other than calibrating Capacity Response to Profits, ensuring the CES credit is being calculated well, and adjusting or removing the variable that adjusts the share of costs that must be covered to be deemed profitable.

One thing that I find non-obvious is when I compare our build rate of a plant type (say, solar PV) to a historical average, and I see we're well-aligned in early years, that doesn't really tell me how much of the solar PV the model ought to be building in the cost-effectiveness section vs. how much it ought to be building in the reliability section. When more is built for cost-effectiveness, less is built for reliability, so they are linked, and in a sense it doesn't matter as the end result is the same - a particular amount of solar PV is built. But this does make it harder to know how much capacity we are expecting the cost-based construction to build, which makes it hard to calibrate Capacity Response to Profits.

robbieorvis commented 1 year ago

Another late one!

A few thoughts:

  1. Megan can respond on the subsidies - it's probably just an issue in the file that can be quickly addressed. The timeline sounds correct though - based on our earlier modeling we expect the tax credits to expire around 2038, which is when we anticipate the system will reach 80% below 2022 emissions levels (as is the requirement in the statute for the IRA).
  2. We'll need to look into onshore wind more. It is interesting this continues to be an issue. I'll see if I can find some more stats that might help explain.
  3. The CES credit is probably correct as is - honestly those markets are pretty simple. Some markets have carve outs for certain types of resources (like solar) with their own solar-RECs that tend to have a much higher price. We can revisit whether or not we want to keep it as tech neutral in the future (since it is based on $/MWh, it does tend to offer more value to resources with higher capacity factors, though).

Regarding evaluating how the model is performing on cost-based versus reliability based additions, here's a suggestion... NREL's Standard Scenarios Viewer has data on capacity expansion for a bunch of scenarios, but notability including a mid-case pre and mid-case post-IRA. We are not going to match either exactly but can use those as guide posts. We should test a scenario with our old tax credit values and see if it is more or less with the pre-IRA scenario, then we can test a scenario with the IRA scenarios and see if it is more or less in line with the IRA scenario. That is a good way to test how the cost-based additions are working, because we expect basically all of the incremental clean energy additions between those scenarios to come from tax-credit driven profitability expansions. Megan or I can help compile the dataset to compare. (It's here: https://scenarioviewer.nrel.gov/)

See plots below as an example (these are total installed capacity. In both scenarios, but especially in pre-IRA, the vast majority of additions are from solar. But even with the IRA, the majority of growth is in solar PV, though onshore wind grows as well. Notably, it does look like onshore wind deployment falls off after 2038 or so.

@.***

robbieorvis commented 1 year ago

One minor thing I noticed: we are double counting output based electricity subsidies in the cost-effectiveness calculations. Cost per Unit New Elec Output already includes both capacity based and generation based incentives. Adding subsidy per unit electricity output into total revenue per unit output by plant type means we are adding that to the plant revenues but we are also subtracting it from the plant costs as part of the cost per unit output. This might be something I accidentally did a while back.

robbieorvis commented 1 year ago

Note also: it's a little hard to accurately project cost-based additions in the future without having the retirements hooked into the capacity calculations, because retirements change market prices and revenues. I'd suggest fixing that and plugging it back in before looking more than a couple of years out in the cost-based additions.

I tried that for now, and it helps. One of the issues I see (after adjusting capacity response to profits) is we probably need a way to reflect the scaling costs of new technologies, like other models do. We talked about this earlier on. We don't need to take this on right now, and Megan and I can look into it while you are out or after, but effectively this would add a penalty to the cost of newer technologies based on the total installed capacity to date relative to what the model wants to add, e.g. if the model wants to add >25% of total installed capacity in a given year, any amount above that gets a 1.5x multiplier added to it reflect labor and supply constraints. I have documentation from the Integrated Planning Model used by NREL to show how they do this. Something to solve at a different time.

On a different point, there are some interesting other things going on, that make sense to me but are a little nuanced. Cost-based additions dominate in the early and mid-2020s as clean energy is profitable to deploy on its own. By the end of the 2020s though, given the amount that's been deployed and the impact to market prices, we see they are no longer really profitable (sort of makes sense...). At that point though we start to need plants for reliability, and the incentives have the effect of tilting the model more towards wind and solar than gas than the otherwise would have without the incentives. So even though we start to see diminishing cost-based additions, we still see a lot of wind and solar being added for reliability purposes, based in part of incentives favoring those techs over gas. This is more or less what we should expect, so happy to see this. For example, we see a lot of merchant (non-utility) plants trying to get online today, but we are also seeing utility IRPs (integrated resource plans) starting to factor in IRA and adding more clean energy. IRPs are largely about meeting reliability needs, so this is all in with what we're seeing in the real world.

mkmahajan commented 1 year ago

The #REF errors were in a section of the tab I'd failed to go back and delete after reformatting our calculations but were not actually being used in the sheet's calculations. I've deleted that section now to clean it up, but the values themselves are unchanged.

Solar PV is a unique resource because we expect it to shift over from claiming the ITC to claiming the PTC once it becomes more cost effective to do so around 2030 (the IRA allows a developer to claim either one, but we need to pick either the generation or capacity credit to apply to all new capacity in a given year). But if you look at the BS-BSpUEO and BS-BSpUECB tabs, solar PV claims one of the two in every year of the model run.

To Robbie's point, I will come back to the subsidy values once we're happy with the electricity sector to evaluate whether we need to extend the subsidies past 2038, depending on when the grid hits 80% below 2022 levels.

robbieorvis commented 1 year ago

Regarding cost adders, here is one example of what EPA does:

4.4.3 Short-Term Capital Cost Adder In addition to the capital costs shown in Table 4-13 and Table 4-16, EPA Platform v6 includes a short term capital cost adder that kicks in if the new capacity deployed in a specific model run year exceeds certain upper bounds. This adder is meant to reflect the added cost incurred due to short-term competition for scarce labor and materials. Table 4-14 shows the cost adders for each type of potential unit for model run years through 2035. The adder is not imposed after 2035, assuming markets for labor and materials have sufficient time to respond to changes in demand. The column labeled "Step 1" in Table 4-14 indicates the total amount of capacity of a particular plant type that can be built in a given model run year without incurring a cost adder. However, if the Step 1 upper bound is exceeded, then either the Step 2 or Step 3 cost adder is incurred by the entire amount of capacity deployed, where the level of the cost adder depends upon the total amount of new capacity added in that run year. For example, the Step 1 upper bound in 2021 for landfill gas potential units is 625 MW. If no more than this total new landfill gas capacity is built in 2021, only the capital cost shown in Table 4-16 is incurred. If the model builds between 625 and 1,088 MW, the Step 2 cost adder of $3,979/kW applies to the entire capacity deployed. If the total new landfill gas capacity exceeds the Step 2 upper bound of 1,088 MW, then the Step 3 capacity adder of $12,639/kW is incurred by the entire capacity deployed in that run year. The short-term capital cost adders shown in Table 4-14 were derived from AEO assumptions.

@.***

I'm not necessarily suggesting we take this approach, but it is intriguing (albeit probably challenging to implement in the EPS). Effectively it just serves as a brake on deployment so the model has marginally increasing costs of deployment, which prevents runaway deployment when technologies are super cheap.

jrissman commented 1 year ago

I fixed the issue where "Subsidy per Unit Electricity Output" was effectively being double-counted in the cost-based capacity construction calculation. The same thing was happening in cost-based retirements, so I fixed it there too.

Given that it sounds like capacity construction is looking decent and we probably need retirements to be more accurate before we could make capacity construction even better, I'll focus on retirements now. The other big thing on my list is reviewing the RPS policy.

Regarding the EPA-style cost adder for scarce labor and materials when building too much of one plant type in one year:

I don't think we are building enough of any one plant type in any one year prior to 2035 to need the EPA-style cost adder reflecting scarce labor and materials, at least not in the BAU case. But also, I am not fond of the idea because it would be going back to an approach we had before that wasn't working for us. Back when we used to use ALLOCATE AVAILABLE for capacity construction, we had to provide input data specifying the area under the curve, which was the amount of capacity that could be built in a single year for each plant type. It was always extremely hard to figure out what the area under the curve should be. This ended up having a strong effect on what got built, whereas we preferred that what got built be based more on costs and capacity factors, not buildable limits. The EPA is using a similar approach (except their quantity is used to decide when to start imposing the surcharge rather than a hard limit on what can be built), but it still involves a need to decide how much capacity can be built in a year before material and labor constraints start to matter. It would amount to re-introducing the need to decide how much capacity is "buildable" in a year. We freed ourselves from having to provide that type of input data when we switched from ALLOCATE AVAILABLE to the logit function, and we considered that a win in terms of making the model more tractable to develop and customize by region.

Also, I don't really think the plant types are independent here, as the same pool of labor that could construct natural gas combined cycle plants might instead be able to construct other plant types. Same with most materials, like concrete, steel, and copper wire. (And power plants represent such a tiny share of the market for these materials anyway, they won't have much affect on national or global prices of the materials.) So I think the EPA's reasoning about scarce labor and materials is suspect anyway. Making per-unit costs more expensive when more units are built in the same year is really just a way of getting diversity in the resource mix of what is built. The logit function introduces a price spread for each plant type and effectively accomplishes this for us in a cleaner way.

jrissman commented 1 year ago

Also, Robbie, when you embed images in an email sent to GitHub, they don't come through. To use embedded images, you'll need to navigate to the GitHub issue page in a browser and drag the image into the text box.

robbieorvis commented 1 year ago

Let's revisit once more of the structure is done and we load in the tax credits. I was seeing >60 GW/year in 2023 each of wind and solar with the tax credits on and some small tweaks I had made. We can reevaluate at that point whether something more is warranted.

robbieorvis commented 1 year ago

Ah good flag. Thank you.

From: Jeff Rissman @.> Sent: Wednesday, May 10, 2023 5:06 PM To: EnergyInnovation/eps-us @.> Cc: Robbie Orvis @.>; Mention @.> Subject: Re: [EnergyInnovation/eps-us] Move to hourly electricity dispatch (Issue #232)

Also, Robbie, when you embed images in an email sent to GitHub, they don't come through. To use embedded images, you'll need to navigate to the GitHub issue page in a browser and drag the image into the text box.

jrissman commented 1 year ago

I built a new cost-based retirement mechanism from scratch and calibrated it in b5cdd61. It's ready for testing.

It still uses the same costs and revenues we calculated earlier. It's still based on profitability, not achieved capacity factor. It just changes the mechanism used to evaluate how much capacity retires due to the costs and revenues it sees. It's a relatively straightforward cost comparison mechanism with the following innovations to make it work better:

Some things haven't changed:

I've calibrated the variable FoSYCRpUNL Fraction of Start Year Capacity Retired per Unit Net Loss so that retirements in the early years are similar to recent historical years (per the spreadsheet I attached to a comment a little ways above). Some of the values range widely, because the net loss per unit output by plant type don't line up well with historical retirements. (For instance, coal has a small net loss per unit output, but historically it has the greatest retirements, so it needed a high sensitivity in FoSYCRpUNL. Natural gas combined cycle had slightly higher net losses than coal, but much lower retirements, so it needed a much lower sensitivity.) If you are able to update or correct any of the profit or loss figures for the plants, you should then update FoSYCRpUNL accordingly to keep retirements reasonably aligned with recent history. For example, I wonder if coal plants may have some costs related to managing their particulate pollution that we're not capturing, which might make them more expensive to operate than it appears from just their fuel, O&M, and forward (annual) capital costs.

Here's a graph of capacity with the new retirements in place. Notice that coal now retires out in the BAU case, whereas before, it only had slight retirements from the BAU retirements schedule.

Cap_With_Retirements

--

Finally, I wanted to note here that I did carefully examine and test the older Weibull-based retirement mechanism before deciding to replace it. It was clever in theory, but it had a number of problems. For one, it would not retire plants that lacked enough revenue to cover their costs since the first condition of the IF THEN ELSE was:

IF THEN ELSE(
  Last Year Revenue to Cover Forward Costs[Electricity Source]
  < Calculated Forward Costs Weibull Location Parameter c[Electricity Source],
  0,

It also had problems with being very sensitive to price differences (so the share it wanted to retire in a year could even jump from 0% to 100% from year to year for some plant types), and it didn't have an easy calibration variable equivalent to FoSYCRpUNL. Also, it was very data-hungry and hard to understand, which would pose challenges for partners and future modelers. Due to these factors, I felt the best choice was to replace it. But I wanted to acknowledge here that I appreciate the work that was done on it.

jrissman commented 1 year ago

On second thought, we might not want to block it from retiring and building the same plant type. Recent retirement rate for NG has been around 2,000 MW/yr, but NG has been built in each of the years when it retires. It used to make little sense to do this in the EPS, but now that we have annual vintages plant properties, it is meaningful to retire and build the same plant type in a year. So we could remove the bit of code that prevents it from retiring a plant type that was built in the prior year. It still works fine without this code.

robbieorvis commented 1 year ago

Looking now - but I agree with removing this feature. It is fine to retire the same plant types in as are being built in a given year... that happens all the time. We just don't want to retire a plant built last year (or before its lifetime is satisfied), which I see you already implemented.

From: Jeff Rissman @.> Sent: Wednesday, May 10, 2023 10:12 PM To: EnergyInnovation/eps-us @.> Cc: Robbie Orvis @.>; Mention @.> Subject: Re: [EnergyInnovation/eps-us] Move to hourly electricity dispatch (Issue #232)

On second thought, we might not want to block it from retiring and building the same plant type. Recent retirement rate for NG has been around 2,000 MW/yr, but NG has been built in each of the years when it retires. It used to make little sense to do this in the EPS, but now that we have annual vintages plant properties, it is meaningful to retire and build the same plant type in a year. So we could remove the bit of code that prevents it from retiring a plant type that was built in the prior year. It still works fine without this code.

- Reply to this email directly, view it on GitHubhttps://github.com/EnergyInnovation/eps-us/issues/232#issuecomment-1543215336, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK5N6SLMJDDRHJFIR4ZJQ5LXFRDITANCNFSM5W3M6AJQ. You are receiving this because you were mentioned.Message ID: @.**@.>>

robbieorvis commented 1 year ago

The current retirements approach is clever and in some ways is similar to other models I've seen, like GCAM and EnROADS. It is looking really good, with a few small data tweaks:

With the data tweaks above, the results look very reasonable. I calibrated against ReEDS (pre/post IRA), AEO (pre/post IRA), and Rhodium's Taking Stock projections (pre/post IRA). We are closest to Rhodium, but that is partly because ReEDS doesn't have endogenous nuclear retirements and EIA's NEMS is very conservative.

I'd like to do some testing around different tax credit scenarios (in particular looking at results before/after IRA tax credits) to see how are things are looking - Megan, perhaps you can help with that and reporting on results for both retirements and capacity additions.

I'd like to also revisit some of our data, e.g. on nuclear costs, and see if we aren't too low on that now and to see if I can find a citation for our ETLE value.

One thought on when to block retirements of certain plant types: One thing I've seen in NEMS is that it when there is a need for new capacity (say for reliability) and a plant type wants to retire, it will only allow it to retire if it is chepaer to build and operate the new one over the life time than to maintain the existing one over the remaining time. For example, if the ongoing cost per unit MWh for gas was $20/MWh and solar was $25/MWh and need for reliability, the model would prevent the gas from retiring so it's not increasing the system cost for reliability. There might be a way to implement something similar in the retirement part of the model as a way to avoid over-retirement (kind of like what you tried) but it might also not be necessary. I will defer to you for now, though I think it might be a good idea to have some way of ensuring the model isn't retiring resources that are uneconomic on a profit basis but more economic than a new resources being built for reliability.

Pending the testing on tax credit impacts, I think we might be able to call this part of the model tentatively done, and move on to the CES portion.

One final comment: We should add a note that subsidies per unit output are captured in lower dispatch costs per unit output and are fed into profitability calculations that way (I had to track this down). This is just to add clarity that they are correctly captured

From: Jeff Rissman @.> Sent: Wednesday, May 10, 2023 10:12 PM To: EnergyInnovation/eps-us @.> Cc: Robbie Orvis @.>; Mention @.> Subject: Re: [EnergyInnovation/eps-us] Move to hourly electricity dispatch (Issue #232)

On second thought, we might not want to block it from retiring and building the same plant type. Recent retirement rate for NG has been around 2,000 MW/yr, but NG has been built in each of the years when it retires. It used to make little sense to do this in the EPS, but now that we have annual vintages plant properties, it is meaningful to retire and build the same plant type in a year. So we could remove the bit of code that prevents it from retiring a plant type that was built in the prior year. It still works fine without this code.

- Reply to this email directly, view it on GitHubhttps://github.com/EnergyInnovation/eps-us/issues/232#issuecomment-1543215336, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK5N6SLMJDDRHJFIR4ZJQ5LXFRDITANCNFSM5W3M6AJQ. You are receiving this because you were mentioned.Message ID: @.**@.>>

mkmahajan commented 1 year ago

I'm committing a few changes on top of Robbie's now. I calibrated the capacity response to profits to a higher value and the new retirement calibrating variable to a lower value (necessary once I disconnected the section of code that blocked retirements of plant types built in the preceding year).

The current values put us somewhere between ReEDS and Rhodium when we use pre-IRA subsidy values, with Rhodium being more optimistic than ReEDS in terms of clean electricity deployment. When I use the IRA subsidies in our BAU, our results are quite similar to the ReEDS IRA case.

One thing I'll note is that I tested this with smoothed gas prices. If I leave in our old gas prices, you see a spike in the "Estimated CES Credit Price," which flows through to the profitability calculations for all CES qualifying resources including nuclear. This causes nuclear to be profitable for just a few years, leading to one year where we get 8 GW of nuclear additions before it becomes unprofitable again. While this may not be an issue once we update our fuel prices file to more current projections that have prices coming down much sooner than our old assumptions, I can get rid of the unwanted nuclear additions if I set the "Share of Costs that Must be Covered to Be Deemed Profitable" to 1. So perhaps we want to subscript this variable by plant type and have the ability to set nuclear (or another plant type) to a higher value if needed. It does seem reasonable that nuclear plants, which require a huge capital investment and would take much longer to build, would require more of their costs to be covered.

Finally, I'll log that Robbie and I were chatting about ideas for tweaking the calibrating parameters to make adaptation for other regions easier. It may be possible to adjust the capacity response to profits by something like the total size of the grid so that the values are endogenous and our calibrated US variables could be adaptable to other regions without having to do a longer calibration procedure. This is something Robbie and I could come back to next week though.

jrissman commented 1 year ago

Today, I didn't have time to focus on the RPS. Instead, I did a number of smaller tasks cleaning up parts of the code and making minor improvements. Only one of them is worth your attention. I formalized the increase in natural gas cost for peakers and steam turbines as fuels/FSPbPPT Fuel Surcharge Percentage by Power Plant Type and incorporated it on the "Fuels" sheet (not the "Electricity - Main" sheet). This will automatically integrate it into all cash flows without new structure in the Electricity sector. Using the "Fuels" sheet, where the fuel properties are all set up, helps us avoid adding more tangled structure to the sector-specific cash flows and means we don't need to track down all the correct spots to incorporate it in the sector.

The fuels/FSPbPPT file cites no source but seems like it could use one, so it might be worth giving it source information and updating the values in it if necessary.

robbieorvis commented 1 year ago

On the issue of nuclear overbuilding...We may need to think about how we can have a longer time horizon for forecasting revenues for new plants, even if it means relying on BAU fuel price files. We should check on the costs for nuclear, but I'd bet we see this issue across multiple plant types that are profitable for a few years from high gas prices but then would revert. That is going to be a persistent issue in any region with a short term change in fuel prices.

From: mkmahajan @.> Sent: Thursday, May 11, 2023 5:41 PM To: EnergyInnovation/eps-us @.> Cc: Robbie Orvis @.>; Mention @.> Subject: Re: [EnergyInnovation/eps-us] Move to hourly electricity dispatch (Issue #232)

I'm committing a few changes on top of Robbie's now. I calibrated the capacity response to profits to a higher value and the new retirement calibrating variable to a lower value (necessary once I disconnected the section of code that blocked retirements of plant types built in the preceding year).

The current values put us somewhere between ReEDS and Rhodium when we use pre-IRA subsidy values, with Rhodium being more optimistic than ReEDS in terms of clean electricity deployment. When I use the IRA subsidies in our BAU, our results are quite similar to the ReEDS IRA case.

One thing I'll note is that I tested this with smoothed gas prices. If I leave in our old gas prices, you see a spike in the "Estimated CES Credit Price," which flows through to the profitability calculations for all CES qualifying resources including nuclear. This causes nuclear to be profitable for just a few years, leading to one year where we get 8 GW of nuclear additions before it becomes unprofitable again. While this may not be an issue once we update our fuel prices file to more current projections that have prices coming down much sooner than our old assumptions, I can get rid of the unwanted nuclear additions if I set the "Share of Costs that Must be Covered to Be Deemed Profitable" to 1. So perhaps we want to subscript this variable by plant type and have the ability to set nuclear (or another plant type) to a higher value if needed. It does seem reasonable that nuclear plants, which require a huge capital investment and would take much longer to build, would require more of their costs to be covered.

Finally, I'll log that Robbie and I were chatting about ideas for tweaking the calibrating parameters to make adaptation for other regions easier. It may be possible to adjust the capacity response to profits by something like the total size of the grid so that the values are endogenous and our calibrated US variables could be adaptable to other regions without having to do a longer calibration procedure. This is something Robbie and I could come back to next week though.

- Reply to this email directly, view it on GitHubhttps://github.com/EnergyInnovation/eps-us/issues/232#issuecomment-1544716488, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK5N6SLR7BYS6TKW4SRBFUTXFVMGHANCNFSM5W3M6AJQ. You are receiving this because you were mentioned.Message ID: @.**@.>>

robbieorvis commented 1 year ago

Also, I'm not sure there's anything wrong with our nuclear costs. They come out to $70/MWh or so in 2021 which is aligned with EIA (NREL uses EIA) at ~50 year lifetime.

However, I will note that the annual ongoing capex piece that we have for existing plants isn't incorporated into the LCOE. I'm going to look into that a bit more and see how EIA does that.

From: mkmahajan @.> Sent: Thursday, May 11, 2023 5:41 PM To: EnergyInnovation/eps-us @.> Cc: Robbie Orvis @.>; Mention @.> Subject: Re: [EnergyInnovation/eps-us] Move to hourly electricity dispatch (Issue #232)

I'm committing a few changes on top of Robbie's now. I calibrated the capacity response to profits to a higher value and the new retirement calibrating variable to a lower value (necessary once I disconnected the section of code that blocked retirements of plant types built in the preceding year).

The current values put us somewhere between ReEDS and Rhodium when we use pre-IRA subsidy values, with Rhodium being more optimistic than ReEDS in terms of clean electricity deployment. When I use the IRA subsidies in our BAU, our results are quite similar to the ReEDS IRA case.

One thing I'll note is that I tested this with smoothed gas prices. If I leave in our old gas prices, you see a spike in the "Estimated CES Credit Price," which flows through to the profitability calculations for all CES qualifying resources including nuclear. This causes nuclear to be profitable for just a few years, leading to one year where we get 8 GW of nuclear additions before it becomes unprofitable again. While this may not be an issue once we update our fuel prices file to more current projections that have prices coming down much sooner than our old assumptions, I can get rid of the unwanted nuclear additions if I set the "Share of Costs that Must be Covered to Be Deemed Profitable" to 1. So perhaps we want to subscript this variable by plant type and have the ability to set nuclear (or another plant type) to a higher value if needed. It does seem reasonable that nuclear plants, which require a huge capital investment and would take much longer to build, would require more of their costs to be covered.

Finally, I'll log that Robbie and I were chatting about ideas for tweaking the calibrating parameters to make adaptation for other regions easier. It may be possible to adjust the capacity response to profits by something like the total size of the grid so that the values are endogenous and our calibrated US variables could be adaptable to other regions without having to do a longer calibration procedure. This is something Robbie and I could come back to next week though.

- Reply to this email directly, view it on GitHubhttps://github.com/EnergyInnovation/eps-us/issues/232#issuecomment-1544716488, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK5N6SLR7BYS6TKW4SRBFUTXFVMGHANCNFSM5W3M6AJQ. You are receiving this because you were mentioned.Message ID: @.**@.>>

jrissman commented 1 year ago

Alright, it was another long and productive day. But there simply wasn't enough time today to completely finish the RPS policy.

One thing I did was add an RPS-specific dispatch pass. We cannot rely on the "zero- and negative-cost dispatch pass" to hanlde this because there are RPS-qualifying resources that are not zero-cost (like nuclear), and there is the possibility that subsidized fossil fuel plants could achieve zero or negative dispatch cost in some regions. Therefore, a dedicated pass is necessary to correctly handle RPS dispatch. I would have sequenced the RPS dispatch pass first, except the guaranteed dispatch pass can include RPS-qualifying resources, which we would want to prioritize ahead of non-guaranteed RPS-qualifying resources in the event that we have more RPS-qualifying output than we have demand in a given timestep. So, I made the guaranteed dispatch pass compatible with the RPS, then slotted in the RPS dispatch as the second pass.

I also did extensive cleaning and fixing of things to prepare the way for analyzing the RPS, such as removing some duplicative electricity demand calculations that fed into it. I also fully checked the code that determines the amount of new RPS-qualifying output to seek, and it looks okay.

Here's a graph of three diagnostic variables showing how the actual amount of RPS-qualifying generation (in red) lags behind the amount the model expects to get from what it wants to build plus surviving capacity (in blue). It's still way above the legal requirement (in green) until the last few years. The red line should be closer to the blue line, maybe exactly on top of the blue line. I will be able to finish debugging this, but I simply need more time.

Graph

There's still some electricity sector clean-up and items left to do, much of which I listed in this comment, plus there is power plant retrofitting. But right now, I think the electricity sector is working well enough that you can merge branch #232 into develop and then work on AEO 2023 data updates and various other GitHub issues in other sectors. I'll resume working on this when I can.

I think being able to devote most of my time to this for a little over a week pushed the electricity sector a whole lot farther along, but it wasn't enough time to take it entirely across the finish line. Still, I'm really happy with the rate of progress we achieved working together over the last six or so days.

robbieorvis commented 1 year ago

I'm just revisiting this, since after talking to Mike he suggested that this might be one of the most important features to account for in the power sector, i.e. having something that serves as a brake on deployment as things scale.

One approach that is different (but perhaps learns from) the EPA's is to use a looping function where the prices scale as a function of deployment until the model converges on a price. For example, you could imagine a for loop that iterates on an LCOE through profitability function until it converges to a price and deployment level. The price could have a multiplier that is based on the calculated deployment, and could in turn be a function of the ratio of that pass's deployment to the max historical (which we now have in the model based on the SYEGC data for historical years, and this could update over time).

I know this seems like a tedious step here but I have been struck but 1) how much Mike has emphasized the need for something like this and 2) what happens in the model without this (sometimes we get unconstrainted/unrealistic deployment). I am happy to chat more about this and not wedded to a specific approach, but I do think we need some iterative process for scaling prices based on how much the model wants to deploy and what the maximum historical deployment is.

I'm around later this week to touch base on this and talk through it, if helpful.

From: Jeff Rissman @.> Sent: Wednesday, May 10, 2023 5:04 PM To: EnergyInnovation/eps-us @.> Cc: Robbie Orvis @.>; Mention @.> Subject: Re: [EnergyInnovation/eps-us] Move to hourly electricity dispatch (Issue #232)

I fixed the issue where "Subsidy per Unit Electricity Output" was effectively being double-counted in the cost-based capacity construction calculation. The same thing was happening in cost-based retirements, so I fixed it there too.

Given that it sounds like capacity construction is looking decent and we probably need retirements to be more accurate before we could make capacity construction even better, I'll focus on retirements now. The other big thing on my list is reviewing the RPS policy.

Regarding the EPA-style cost adder for scarce labor and materials when building too much of one plant type in one year:

I don't think we are building enough of any one plant type in any one year prior to 2035 to need the EPA-style cost adder reflecting scarce labor and materials, at least not in the BAU case. But also, I am not fond of the idea because it would be going back to an approach we had before that wasn't working for us. Back when we used to use ALLOCATE AVAILABLE for capacity construction, we had to provide input data specifying the area under the curve, which was the amount of capacity that could be built in a single year for each plant type. It was always extremely hard to figure out what the area under the curve should be. This ended up having a strong effect on what got built, whereas we preferred that what got built be based more on costs and capacity factors, not buildable limits. The EPA is using a similar approach (except their quantity is used to decide when to start imposing the surcharge rather than a hard limit on what can be built), but it still involves a need to decide how much capacity can be built in a year before material and labor constraints start to matter. It would amount to re-introducing the need to decide how much capacity is "buildable" in a year. We freed ourselves from having to provide that type of input data when we switched from ALLOCATE AVAILABLE to the logit function, and we considered that a win in terms of making the model more tractable to develop and customize by region.

Also, I don't really think the plant types are independent here, as the same pool of labor that could construct natural gas combined cycle plants might instead be able to construct other plant types. Same with most materials, like concrete, steel, and copper wire. (And power plants represent such a tiny share of the market for these materials anyway, they won't have much affect on national or global prices of the materials.) So I think the EPA's reasoning about scarce labor and materials is suspect anyway. Making per-unit costs more expensive when more units are built in the same year is really just a way of getting diversity in the resource mix of what is built. The logit function introduces a price spread for each plant type and effectively accomplishes this for us in a cleaner way.

- Reply to this email directly, view it on GitHubhttps://github.com/EnergyInnovation/eps-us/issues/232#issuecomment-1542807257, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK5N6SK2W3G2OBMI3ANBXVLXFP7DLANCNFSM5W3M6AJQ. You are receiving this because you were mentioned.Message ID: @.**@.>>

mkmahajan commented 1 year ago

One of the remaining pieces of this issue we had left was tracking T&D costs based on electricity system growth rather than through a BAU file. I pushed a series of edits that adds the following:

jrissman commented 1 year ago

Today, I rebuilt the RPS/CES structure to improve its accuracy, which is particularly important at high RPS values (approaching 1), where it is able to achieve the RPS within a small margin of error, rather than stalling out at 90-95%. It is also simpler and easier to understand than the old method, and it deploys RPS-qualifying sources somewhat more smoothly over time.

Old method, 100% RPS setting RPS_old

New method, 100% RPS setting RPS_new

I didn't have time to get to any other things in the EPS. It's already past 11 pm as I'm posting this.

robbieorvis commented 1 year ago

Hi Jeff - please see my other email regarding this. Unfortunately I think it is not working as expected and that the capacity response to profits was masking this.

jrissman commented 1 year ago

The fix was actually pretty simple - it was a minor fomula error corrected in 898166d. But I noticed that when the code is working correctly, at a 100% RPS setting in 2050, it reaches the 100% RPS setting quite a bit sooner than that. That's because it is trying to smoothly approach compliance in all hours (which it will need in 2050), but this makes it over-comply in some earlier hours. Notice how the blue line goes up to touch the red line by the early 2040s.

Elec_Gen_RPS

While some degree of overcompliance is desirable, I thought this seemed too aggressive. I decided to try to reduce the need to comply in hours of RPS-qualifying generation shortfall by the amount of RPS-qualifying excess generation (i.e., above the RPS requirement percentage) in other hours. This turned out to be quite difficult, much more than simply fixing the bug. A straightforward implementation that reduces the need in shortfall hours by the full extent of the surplus in surplus hours results in very spiky and unrealistic behavior. I ended up building a mechanism that reduces the need to build to meet the RPS in non-complying hours by the extent of the over-supply in other hours, but limiting the maximum possible reduction in demand for new RPS-qualifying resources, so the model can consistently make progress toward the ultimate RPS value. This is in commit 8d40b0a. Notice in the following graph the blue line no longer reaches the red line until around 2050.

Elec_Gen_RPS2

The electricity dispatch stacked area graph is very smooth and nice when viewed with Capacity Response to Profits set to zero. The stacked areas become slightly lumpy when the cost-driven capacity construction is enabled, but that may not be the fault of the RPS.

While the bug fix in 898166d is an obvious improvement, I have somewhat mixed feelings about the mechanism added in 8d40b0a to reduce over-compliance. It's probably better than without it, but I wish there had been something more elegant instead.

I think the way it tends to over-comply with the RPS now unless constrained tends to make RPS foresight no longer useful. If we are absolutely certain we don't want it, we could consider removing RPS foresight from the model, which would simplify the code.

robbieorvis commented 1 year ago

Thanks for this, Jeff.

This is a great step in the right direction. Setting aside the compliance issue for a minute... the model still has an issue that is resulting in nonsensical results. My take is that it primarily stems from the use of average capacity factors instead of marginal capacity factors in determining the hourly price in every year. This results in the model needing to iteratively deploy a lot more than it really should, because it's finding almost zero contribution to meeting demand after it adds what it thinks are the most cost effective resources. This really only comes into play late in the model run as we approach 100%.

For example, you can see the massive increase in deployment in 2049 to get to 100% of about 300 GW each of wind and solar and 600 GW total.

Taking the last year smoothed achieved capacity factors the main reason for this, I believe.

Consider that even accounting for declining emissions factors from saturation, you get the fleet average. Let's say you have 100 GW of wind with a capacity factor of 40% in every hour, but you are fully saturated in all the hours. Adding 10 more GW with a zero capacity factors just drops the average to (.4*100/110 = 36%), but in reality adding the 10 GW of wind gets you nothing additional. This is sort of what is happening the model but instead of the marginal units having a capacity factor of 0%, it's more like 0.5%, so the model sees that it adds some, but it isn't achieving the desired outcome, and so it iteratively builds more and more until it can meet the demand, even though it has chosen a pathway that is much more expensive than other options where the power is dispatchable, i.e., maybe it only needed 25 GW of output to meet the RPS across all hours, but is built 600 GW of wind and solar instead of 25 GW of dispatchable clean plants.

I think the cleanest way to do this is to attempt to calculate marginal plant capacity factors. Another idea that comes to mind though is to use the for loop to somehow optimize for total cost (MW*$/MW) but I'm not entirely sure how we would do that. The marginal cost approach seems like an elegant workaround, but might be computationally tricky.

The above might affect compliance, so I'll hold on comments to that until we are able to address the above.

jrissman commented 1 year ago

Thanks, Robbie. That is something I (or someone else) can look at regarding marginal capacity factors when there is time.

Prior to you posting your latest comment, I made an illustration to better illustrate the current approach to lessening over-compliance. The core challenge here is that at a 100% setting, we need to comply with the RPS in all hours, but at lesser RPS settings, it's okay to have some hours over-comply and make up for hours that under-comply. Not all hours have equal "weight" because hours in Electricity Timeslices with more days are weighted by the number of days in that timeslice, while hours in a peak day timeslice only count once. I first tried approaches that converted to bulk generation by multiplying by days per timeslice and reducing the shortfall by the amount of excess generation, but then you lose information about what timeslice and hour the demand or excess originally came from. The solution I came up with was to find the totals and subtract the excess from the shortfall to find the "net shortfall" as illustrated in this drawing:

2023-06-15 RPS Excess to Make Up Shortfall Illustration

We can then take a ratio of the net shortfall to the total shortfall to get a multiplier that reduces demand for RPS-qualifying generation. We then apply that multiplier to all hours in all timeslices, which means we don't need to allocate the reduction in demand by timeslice and by hour (since we never lost that information in the first place), and it doesn't matter if some timeslices have more days than other timeslices, since the shortfall (in any hours with a shortfall) is being reduced by the same percentage. (I converted the "multiplier" to a "reduction" using one minus the multiplier because the variable naming is clearer that way, but it's mathematically identical.)

I thought it was super elegant and ought to perfectly allow the hourly results with the annual RPS requirements. But in practice, it made jagged sawtooth results. The "maximum possible reduction" of 80% was my solution. It ensures the model at least makes 20% of the progress toward the RPS in non-complying hours that it would have wanted to make those hours comply, even if there is enough excess in other hours to technically mean nothing is needed for the non-complying hours. This ensures the model slowly has all hours approach compliance, avoiding the crazy sawtooth effects when the RPS tightens enough that the excess no longer covers the shortfall in the non-complying hours.

While I like the elegance and simplicity here, I was a little disappointed I needed to put in that 80% maximum reduction, because I thought it should work on a theoretical level without that. I wondered if in the years where it ran out of sufficient excess and needed to build something, it was massively over-building, leading to another group of years where nothing gets built, then another sudden jump when the excess runs out again. The code that builds plants to make up the shortfall was not designed with this "net shortfall" in mind, so maybe when that code gets invoked, it over-builds.

The concept of what it's doing isn't that difficult now that I've laid it out here, so someone else could consider tinkering with it when you have time. But it might not make sense to worry about this mechanism to prevent overbuilding if we're going to be using something different for the capacity factor instead of Smoothed Achieved Capacity Factors because the capacity factors are upstream of the excess-makes-up-for-shortfall mechanism. Still, I wanted to document the mechanism here and now, while it's fresh in my mind, so we can examine it later and see if we can maybe get rid of the 80% maximum reduction cap someday, if we want to.

robbieorvis commented 1 year ago

I spent a bit more time looking into this today.

The issue I originally saw is actually related to the smoothing function, which has values of 0.8 in all years but then a 0 in 2050, so the model is playing catch up in that year. Not sure if that is just a formula error or a methodological one, but fixing that resolved at least the final year issue, but then reintroduces the over compliance issue. I have not dug into over compliance, but one thing I want to make sure is that we aren't attributing over compliance in hours where the grid is already at 100% of supply renewables. I did also add an adjustment that changes the RPS hourly costs by the ratio of last year achieved capacity factor to expected hourly capacity factor. I haven't committed or pushed anything yet.

With the above correction, the model can theoretically achieve 100% clean in the later years. However, I still wonder about the structure of what it is building and tradeoffs between technologies. For example, would it be cheaper to build 1 MW of geothermal in hour zero instead of wind if that same 1 MW could be used at noon when solar PV is being built? In essence I'm getting a little tripped up on the different between LCOEs and deployment for a given hour versus total system costs, and I'm little worried the model is choosing hourly LCOE instead of total system costs and that the results are therefore not quite right. The reason for all of this is that the model is overbuilding a ton (curtailment rates of 30-40%) and satisfying demand with 100% wind, solar, nuclear, and hydro. Given the levels of stuff being deployed, I am also starting to wonder if we will need resource curves to scale capacity factors for new renewable resources by their total installed capacity. We can revisit that once we get the RPS working.

I'll try to spend some more time on this next week, if I can find the space to do it.