MimiDICE2016 5-year pulse?

Hi!

Just wanted to reach out to check – I noticed that in DICE2016, in the marginaldamage.jl file, it says in a few places that the pulse is being implemented as a 5-year pulse:

e.g. in function _compute_scc (line 49):

scc = sum(df .* marginal_damages * 5) # currently implemented as a 5year step function; so each timestep of discounted marginal damages is multiplied by 5

in function get_marginal_model (line 63):

mm = create_marginal_model(m, 5 * 1e9)    # 1 GtCO2 per year for 5 years, so 5 * 10^9

However, in the add_marginal_emissions function, it references a 10-year pulse?

addem[time[year]] = 1.0     # 1 GtCO2 per year for ten years

Is this a typo? Otherwise, how does this work?

Thanks!

@tammyt123 good question, I’m not positive but I’m guessing this has to do with the fact that DICE2013 and DICE2016 use 5 year timesteps, but DICE2010 used 10 year timesteps, so perhaps this is a leftover typo … because as you say addem[time[year]] = 1.0 is only putting added emissions into one timestep.

@ckingdon95 any insights here?

1 Like

Thanks, Lisa!

Theoretically, if I were to manually modify DICE2016 such as to implement a 1-year pulse, could I just make a change in the mm = create_marginal_model(m, 5 * 1e9) code where I set the delta to 1e9 instead of 5*1e9? Or would this have other ramifications?

More specifically, if I “input” a temperature change vector into the damage function of DICE2016 that corresponded to the temperature change from a 1 year pulse, could I just make the above change to correct for the fact that it’s no longer a 5-year pulse, or do I have to make other adjustments too? I was playing around with the delta and as far as I could see, the effect was just to divide damages by delta.

Thanks so much again!

Hi @tammyt123 good questions! Just to better understand the use case you have here, are you planning to modify DICE so that instead of internally calculating the temperature change over time, you exogenously force it with your own temperature change vector? In that case would you have one vector for a consistent baseline and one for the effects of a 1 year pulse?

1 Like

Hi @lrennels, yes – exactly that!

Great, let me look this afternoon and I’ll respond. My gut reaction is that if you are doing it this way you’re not going to use the create_marginal_model type functions at all, because the way those operate is by adding CO2 and then running the normal DICE with it’s internal temperature feedbacks etc.

I think what you’re going to want to do is just run two runs of DICE with your exogenously forced temperature substituted for the way it’s normally calculated within DICE, and then take the difference of damages and discount how you wish. I can give more specific code for this type of Mimi operation this afternoon!

You can always change the pulse size, as you indicate above, but since DICE runs on five year Timesteps you can’t really use that infrastructure to say the pulse is only spread over one year since this only calculates effects each five years, if that makes sense? But if you have your own temperature vectors we can force DICE exogenously with those.

1 Like

Ok so I believe the very long way around my answer is below, which is that I think what you’re doing is correct, I just didn’t fully understand it before.

First you pull in your packages and set up a vanilla version of MimiDICE2016:

using Mimi
using MimiDICE2016
m = MimiDICE2016.get_model()

Next you call the Mimi function to set the parameter :TATM to your exogenous temperature

set_param!(m, :TATM, input_temp)

This assigns the values in your input_temp vector to the parameter :TATM in any components with that parameter. In this case the only component is :damages, and you are disconnecting that parameter from the climatedynamics variable :TATM and instead forcing exogenously. Also just note you’ve left the tatm0 parameter alone so it is 0.85 but perhaps that’s fine it’s just your initialization year.

Ok great so at this point you create a marginal model mm.

mm = Mimi.create_marginal_model(m, 1e9)

This basically copies m twice into mm, such that mm contains two models, mm.base and mm.marginal, which at this case point are identical. You’ve also set the delta to be 1e9 so that it knows in the future to divide by that when calculating differences between the two models. By that you are saying your temperature change is as a result of a pulse of 1GtC.

Note here something a bit confusing … MimiDICE has it’s own MimiDICE2016.get_marginal_model which also modifies the emissions. You were smart, don’t use that one, because you don’t want to modify emissions. You are going to exogenously create a different in damages using two different temperature vectors, that is the difference in damages you are looking to see (if I understand correctly).

Anyways, next you said you call the following set_param!(mm, :TATM, new_input_temp) which can’t quite be right since that will error, but here’s what I think you’re doing and what I would do.

update_param!(mm.marginal, :TATM, new_input_temp)
# or set_param! but update_param! is more accurate since there already is one

This will update the :TATM parameter in the :damages component (and any other components with that parameter but there are none here) in the modified sub-model of the mm marginal model.

Now you run the following using the sneaky _compute_scc helper function that the creators didn’t advertise (commented with #“helper function for computing SCC from a MarginalModel, not to be exported or advertised to users”) … but of course you seem to understand what it’s doing so feel free to use it!

scc = MimiDICE2016._compute_scc(mm, year = pulse_year, last_year = 2300, prtp = 0.03, eta = 0.0)

and assuming that the times line up with when your pulse occurred in your exogenous temperature vector is correct, you should be good, except as you said you need to divide by 5 if you want to use this function, because you don’t need to adjust for your pulse being spread out across a timestep. You are correcting for this final line:

scc = sum(df .* marginal_damages * 5)  # currently implemented as a 5year step function; so each timestep of discounted marginal damages is multiplied by 5

Or you can write a local copy of the function without the division.

Thus I think you’re doing the right thing, but please tell me if any of this is confusing. Overall I would suggest sanity checking your values, and also perhaps writing your own longer function doing exactly what you want and make sure the values match, just as good practice since I didn’t write up DICE2016 myself nor do I know where your temperature vectors came from :slight_smile:

2 Likes

Thanks so much for this, Lisa!

Yes – re: the set_param! line, I had actually meant

MimiDICE2016.set_param!(mm.modified, :TATM, new_input_temp)

but I’ve changed this to update_param! as per your suggestion.

So if I were to implement this with a temperature vector corresponding to a 5-year pulse, would I do the same as above but leave the pulse size as 5*1e9 and leave MD multiplied by 5 in the SCC function?

For the 1-year pulse, when I don’t multiply MD by the end at the end, the number becomes very small. Don’t I still need to multiply by 5, or at least interpolate marginal damages in some way, due to the fact that the vector of marginal damages at the end only corresponds to MD for each 5-year timestep (i.e. damages in 2030, 2035, 2040) – I think?

Also, given all of this, do you have thoughts on which method – 1 year or 5 year – is more “appropriate” / accurate to use given the framework? From my experimentation, it seems that the “1-year pulse” gives higher SCC numbers than the 5-year (around 20% higher). My theory is that this is because of implementation of the SCC / damages as a step function?

Hi @tammyt123 yes I guess you are right, you would still need to multiply by five since the vector only corresponds to each 5-year timestep, so you would just not divide by 5 at the end since it was a 1 year pulse instead of 5 x 1 year pulses.

My expertise doesn’t lend a good answer to the second question, but I will see if any of my close colleagues have some intuition or just a good resource/paper/etc. to direct you too!

1 Like

Hi @tammyt123,

First of all, DICE2016R2 is now public here, but very much a work-in-progress because the team is still testing to get the Mimi outputs to match the GAMS model outputs. The damage fraction alpha parameter was updated, and the MIU and S optimized time series pulled in from the new GAMs runs, but still trouble-shooting.

Secondly, I confirmed with others that the only parts of the compute_scc type code you need to alter for using an exogenously forced pulse is anything to do with pulse_size being distributed/divided etc. which you have done. As we found, multiplying by 5 (or doing some fancier interpolation if you want) is still necessary.

When it comes to the 1 vs 5 year pulse, there are a few things to consider. Two of the choices being made are (1) how much time the pulse is spread over (2) pulse size.

I believe that historically the IAMs did not respond as quickly to a pulse, so the choice of (1) wasn’t as consequential as you may be finding with the model you are using to create temperature change (is it FAIR?). Intuitively, you should be going for a 1-year pulse, since that’s what the number is getting at, so using 1-year when possible is advisable … but it may be worth experimenting with just how sensitive something like FAIR is to that choice, and seeing what conclusions can be drawn about these newer generation climate modules of IAMs.

For (2), it is important to make sure that the SCC response isn’t highly sensitive to the pulse size used to calculate the SCC. For example, assuming a one-year pulse, you could graph pulse size on the x-axis and SCC on the y-axis, and look for where the values stabilize (they will likely be unstable with very small pulses and very large pulses), and use a pulse size in the middle of that stable portion. This kind of thing was done historically for models like DICE, FUND, and PAGE.

Let me know if that makes sense!

We’re also working on an example notebook of how to couple FAIR with FUND right now that should be published this week, which isn’t quite possible with DICE2016 since that has 5-year Timesteps and FAIR has 1-year Timesteps, but it might interest you.

1 Like

Thank you so much for this follow up and the suggestions, Lisa! Really appreciate it once again. And would definitely be interested in the FUND x FAIR example notebook once published. Thanks for the heads up!

Hi @lrennels – I actually had a quick follow up question. You say that coupling FAIR with DICE2016 “isn’t quite possible” because of the timestep differences between the models. What do you mean exactly by this? Would it not be possible, as long as you align the annual temperature output from FAIR with the DICE timesteps (i.e. pull out the temperature every 5 years) and input this into the DICE damage function? Or would this be inaccurate / inappropriate? Thanks again!

Hi @tammyt123, it sounds like what you are doing by "coupling’ is exogenously forcing the DICE2016 model with FAIR temperature series. That’s fine, but what you’re missing there is any feedback between FAIR and DICE, which is really what I think most experiments would want to have in order to properly couple them i.e. have FAIR handle certain parts of the timestep like calculating temperature, and then have DICE handle the more complicated socio-economic steps of emissions and damages, and then feed results back into FAIR for the next timestep.

I’m doing that direct connection for FUND and FAIR, which are both annual, and it would be doable with DICE as well but you’d need to convert DICE to annual timesteps (which I think someone has done somewhere) since you can’t have components in one model running with different timesteps lengths.

Does that make sense?

2 Likes

Ah yes, that makes sense. Thanks so much Lisa!