Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adjust calculation of PRICE_EMISSION #726

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open

Adjust calculation of PRICE_EMISSION #726

wants to merge 26 commits into from

Conversation

OFR-IIASA
Copy link
Contributor

@OFR-IIASA OFR-IIASA commented Jun 30, 2023

This PR revises the calculation of the PRICE_EMISSION to correctly account for the period-duration when deriving the period-specific PRICE_EMISSION from a single marginal value which covers multiple periods (cumulative constraint).

How to review

  • Read the diff and note that the CI checks all pass.
  • Build the documentation and look at revised documentation section.
  • Ensure that changes/additions are self-documenting, i.e. that another
    developer (someone like the reviewer) will be able to understand what the code
    does in the future.

PR checklist

  • Continuous integration checks all ✅
  • Add or expand tests; coverage checks both ✅
  • Add, expand, or update documentation.
  • Update release notes.

Note

I'm editing this description to keep track of what has happened in this PR and what's still required. My edits are restricted to below this point.

Notes on progress

On January 22, 2025, @glatterf42 went through the posts here and marked everything as outdated that's no longer relevant to finishing this PR. Several things are still outstanding:

  • The documentation might hold warnings about this issue or explanations about how PRICE_EMISSION is calculated and needs to be updated in these cases.
  • The release notes need to be updated, too.
  • The tests are not sufficient and not passing.

This last point is really the main issue here and the focus of (almost) all posts not marked as outdated: what kind of test do we want to have for the PRICE_EMISSION feature and and how we we write it so that it passes?

@OFR-IIASA outlined five test cases that would be sufficient and four of them are already present and passing. However, the fifth case is still missing, even after several tries by @behnam-zakeri, @yiyi1991, and @glatterf42. The jupyter notebook in this PR was just used for testing and debugging this work, and should be dropped before merging this PR to main.

Thanks to discussions with the MESSAGE team, we believe that the issue lies in the complexity of the scenarios used for testing. When using the PRICE_EMISSION fix at hand with full global models, both @OFR-IIASA and @yiyi1991 confirmed that the fifth test case is passing fine. Thus, our assumption is that the test is not passing here because the scenario used is too simple: for example, with an insufficient number of technologies defined, the model might decide to use the cheap, high emission technology as long as it can before switching suddenly to the expensive, low emission technology. It seems that such pointwise behavior (as opposed to a shift on a continuum of technologies) causes the duality we want to test for to no longer hold.

One possible workaround is to use a global model for testing -- and we already do that: our nightly tests load the full global snapshot from zenodo. Since this is a large model, that takes a significant amount of time, about 45 minutes (estimated by @yiyi1991). Thus, we don't want to include this in our regular CI suite. We could instead think about migrating this test to the nightly tests, where more runtime is not all that problematic.
Alternatively, we could also invest some time to develop/fine-tune a scenario just for this missing duality test.

@OFR-IIASA OFR-IIASA self-assigned this Jun 30, 2023
@OFR-IIASA OFR-IIASA added the bug Doesn't work as advertised/unintended effects label Jun 30, 2023
@OFR-IIASA OFR-IIASA added this to the 3.8 milestone Jun 30, 2023
@gidden

This comment was marked as outdated.

@glatterf42

This comment was marked as outdated.

@glatterf42

This comment was marked as outdated.

@glatterf42 glatterf42 modified the milestones: 3.8, 3.9 Dec 13, 2023
@glatterf42

This comment was marked as outdated.

@behnam-zakeri
Copy link
Contributor

behnam-zakeri commented Mar 28, 2024

@glatterf42 thanks for following up on this in today's meeting. I went through the test and actually the tests are not using Westeros tutorials but building new models for testing this.

I started to test the changes in this PR using Westeros tutorials. I followed the logic below:

  • Clone a "baseline" (scenario I) to a new "emission bound" scenario (scenario II) and add a binding emission bound to it.
  • Solve scenario II and calculate emission prices (here PRICE_EMISSION_NEW).
  • Add the same prices as tax_emission to a clone of "baseline" and call it "emission tax" scenario (scenario III).
    Check the following:
    a. Emission prices are equal for scenario II and scenario III (looking into PRICE_EMISSION_NEW)
    b. Emissions are equal for scenario II and scenario III (looking into EMISS)

I ran this setup for two types of emission bounds, cumulative and yearly, using Westeros tutorials. My observation is that:

  • In both cases, check (a) passes.
  • In both cases, check (b) does not pass.

So, my first reaction is that:

  1. Is the above logic for testing this PR correct?
  2. Why shouldn't we use Westeros tutorials for testing this? (In other words, what changes are brought with the new test that the tutorials cannot replicate?)

Maybe @OFR-IIASA and @gidden can help here.

@gidden
Copy link
Member

gidden commented Mar 28, 2024

Hey @behnam-zakeri - thanks so much for looking into this so quickly. I have a comment and two questions:

  1. In principle there's no reason not to test with the tutorials, in fact I think it's a great idea.
  2. Can you check how close the objective functions are between the two runs? They should be within game solution tolerance
  3. Is the emission bound cumulative or year on year? If cumulative, I'd suggest trying year on year. In principle we may have an issue with hotelling calculations for this

@OFR-IIASA
Copy link
Contributor Author

Dear Behnam,

As there were existing test, the idea was to leave those in place and extend the test examples covered, while also making sure that the results are the SAME as opposed to just "close". The test for "duality" was "close" before, but the allowed tolerance was too large, hence the error in the formulation wasnt caught.
Discussing with Volker at the time when changing the formulation for deriving the price, the idea behind the test was to set up a a simple example with a single technology that has emissions and add-on technologies which would the allow for emission reduction. In the end, I never really got to completing the work, hence the tests dont pass, and this was the main task left to do in the PR, i.e. develop a test case that works.

As you rightly point out, there are a few different cases to test, but I have made a few more adjustments below.

TEST 1.: Equal length time periods // CUMULATIVE bound
-> Clone a "baseline" (scenario I) to a new "emission bound" scenario (scenario II) and add a binding CUMULATIVE emission bound to it.
-> Solve scenario II and calculate emission prices (here PRICE_EMISSION_NEW).
-> Check: that calculated emission price calculation matches a manual calculation of the price based on the EMISSION_EQUIVALENCE

TEST 2.: Unequal length time periods // CUMULATIVE bound
-> Same as "TEST 1", but with changes in the period duration, so that there are 5 and 10 year timesteps.

TEST 3.: Equal length time periods // PERIOD bound
-> Same as "TEST 1", but with period specific bound

TEST 4.: Unequal length time periods // PERIOD bound
-> Same as "TEST 2", but with period specific bound

TEST 5.: Test price duality
-> Scenario A.) Apply different CUMULATIVE bounds to a scenario with differing timeperiod lengths.
-> Scenario B.) Apply the resulting PRICE_EMISSION from scenario A as tax_emission; check:

  • ensure that EMISS are equal for scenarios A and B
  • ensure that tax_emission and PRICE_EMISSION_NEW as equal in scenario B

-> Scenario C.) Apply the emission trajectory (EMISS) from scenario A as a period specific bound_emission; check:

  • ensure that EMISS are equal for scenarios A and C
  • ensure that tax_emission and PRICE_EMISSION_NEW as equal in scenario C

In the end, we need to ensure that the carbon-price from a cumulative constraint, provides the same emission trajectory per period as the cumulative trajectory when applied as tax_emission (Test 5, scenario B) and that the the emission trajectory per period from the cumulative constraint scenario provides the same carbon price as when applying a cumulative constraint (Test 5, Scenario C).

@behnam-zakeri
Copy link
Contributor

@gidden thanks for the reflection.
As @OFR-IIASA mentioned, it seems Westeros tutorials are not enough, even though they can be part of the test, because they do not exhibit the change in the model periods, e.g., from 5-year to 10-year. My tests using the tutorial were both for year-by-year and cumulative bounds.

@behnam-zakeri
Copy link
Contributor

@OFR-IIASA thanks for the updates and confirming the logic for the tests. I see your point, if we need varying model periods we need to develop a new test model.

@glatterf42

This comment was marked as outdated.

@behnam-zakeri
Copy link
Contributor

behnam-zakeri commented Apr 11, 2024

@glatterf42 I made the following changes and now this PR is ready for reviewing/improving the tests. Indeed, the existing tests are good enough, but a few of them, i.e., test_price_duality don't pass. Here, @OFR-IIASA can confirm that the changes made in this PR in the GAMS formulation work on the global model, but not on the tests developed here, right?

The changes made today are:

  • The GAMS formulation is cleaned up from new variable names and extraneous equations.
  • model.py is cleaned accordingly.
  • test_feature_price_emission.py is cleaned from hard-coded data and temporary text info.
  • "Addon" technologies are disabled in the test, as it seems they don't do anything specific but make the test a bit complicated. The tests behave the same as before without "Addon".
  • Important: For test_price_duality, model periods with equal length are added, and the checks don't pass for them too. So, there is an issue regardless of the model periods being equal or varying between 5 and 10 years.

@glatterf42
Copy link
Member

Still working on getting test case 5 to work. I've tried using make_westeros, but that has its model years ([700, 710, 720]) hardcoded, so doesn't work out of the box.
Instead, I've adapted the model_setup from what @OFR-IIASA started and the tests are starting to run with what look like reasonable results.
In order to troubleshoot the tests, I included tests/feature_price_emission.ipynb, which uses a new local DB instance to avoid cluttering existing ones. Run something similar to ixmp platform add test_feature_price_emission jdbc hsqldb /home/fridolin/.local/share/ixmp/localdb/test_feature_price_emission to use the same name or adapt it as you see fit.
When I'm running this notebook (which for now just includes evenly-spaced years), I find that whatever variable we're fixing works fine, while the other doesn't. So in 5.1, we fix the tax_emission based on PRICE_EMISSION and this is registered correctly, but the EMISS trajectory is off. In 5.2, we apply a period-specific bound_emission, which leads to the same trajectory, but different prices.
In 5.2, the ACT of the technologies seems to be different, while in 5.1, only the the marginals of ACT seem to differ.

This might speak to a problem in the conversion between emission bounds and emission prices.

If anyone has any idea how to troubleshoot this further or even what specifically might cause this problem, I'm happy to take suggestions :)

@glatterf42

This comment was marked as outdated.

@yiyi1991
Copy link

Some reflects to the previous comment from @glatterf42.

We know PRICE_EMISSION is derived from the marginal value of the equation EMISSION_CONSTRAINT (the change in the objective function (total cost) for a marginal relaxation of the constraint). This adjustment was created as PRICE_EMISSION was not processed by the correct discount factors (df_period, df_year).

Then how we would know this is a "correct" PRICE_EMISSION? My understanding of why we are doing this duality test (the set "bound_emission -> PRICE_EMISSION" and the set "tax_emission -> EMISS") is, if the PRICE_EMISSION is "correct", the duality should be reached.

Larger-size scenarios now reach such duality while small-size scenarios are still difficult to reproduce, after another several tries (i.e., adding many technologies (up to 9), adding multiple demands and multiple technologies serving the output of more than one demand, including historical emissions).

We can try an open-source snapshot, putting the following before the tests you have already created.

platform = ixmp.Platform()
snapshot_path = "path_to_downloaded/MESSAGEix-GLOBIOM_1.1_R11_no-policy_baseline.xlsx"

scen = message_ix.Scenario(platform, model="test_emissions_price", scenario="many_tecs", version="new")
scen.read_excel(snapshot_path)

Title: MESSAGEix-GLOBIOM 1.1 R11 no-policy baseline
DOI: 10.5281/zenodo.10514052

Though, as you sensed, the read_excel will take around 45 minutes...and downloading a snapshot is of course manually...

Copy link

codecov bot commented Jan 22, 2025

Codecov Report

Attention: Patch coverage is 10.37736% with 95 lines in your changes missing coverage. Please review.

Project coverage is 24.0%. Comparing base (e0f2e02) to head (61c133e).

Files with missing lines Patch % Lines
message_ix/tests/test_feature_price_emission.py 8.8% 93 Missing ⚠️
message_ix/tests/test_report.py 0.0% 2 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main    #726   +/-   ##
=====================================
  Coverage   24.0%   24.0%           
=====================================
  Files         48      48           
  Lines       4394    4394           
=====================================
  Hits        1057    1057           
  Misses      3337    3337           
Files with missing lines Coverage Δ
message_ix/models.py 84.5% <100.0%> (ø)
message_ix/tests/test_report.py 20.3% <0.0%> (ø)
message_ix/tests/test_feature_price_emission.py 10.7% <8.8%> (ø)

@glatterf42 glatterf42 mentioned this pull request Jan 22, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Doesn't work as advertised/unintended effects
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deviation between PRICE_EMISSION calculation and tax_emission use in OBJECTIVE-function
6 participants