Results

In this page we report the results from running the package with the official scenarios and compare the output of DICEModel.jl with the official GAMS output as given in the DICE 2023 Introduction and User's Manual (v3.1.2, May 15, 2024)

The scenarios considered are:

  • cbopt: The C/B optimal scenario
  • t2c: The temperature constrained to max 2 °C scenario
  • t15c: The temperature constrained to max 1.5 °C scenario
  • altdam: The alternative damage scenario
  • parisext: The Paris extended scenario
  • base: The base (current policies) scenario
  • r5: The scenario with discount rate of 5%
  • r4: The scenario with discount rate of 4%
  • r3: The scenario with discount rate of 3%
  • r2: The scenario with discount rate of 2%
  • r1: The scenario with discount rate of 1%

We start by loading the required packages.

Show code
using Pkg
Pkg.activate(joinpath(@__DIR__,".."))
#Pkg.add(["DICEModel", "CSV","DataFrames","Plots"]) # Run once and comment this back
using DICEModel, CSV, DataFrames, Plots, XLSX
  Activating project at `~/work/DICEModel.jl/DICEModel.jl/docs`

We can now define the scenarios to run and their specific graphical attributed when plotted. Finally we call the function run_dice_scenario with each of them and save the output in the results dictionary (keyed by the scenario name):

Show code
# Scenarios to run:
scenarios = ["cbopt","t2c","t15c","altdam","parisext","base","r5","r4","r3","r2","r1"]
plot_attributes = Dict(
    "cbopt"    => (label="C/B optimal", linestyle=:solid, colour=:red, markershape=:circle, markercolor=:white, markerstrokecolor=:red),
    "t2c"      => (label="T < 2 °C", linestyle=:dash, colour=:green, markershape=:cross, markercolor=:green, markerstrokecolor=:green),
    "t15c"     => (label="T < 1.5 °C", linestyle=:dot, colour=:lime, markershape=:dtriangle, markercolor=:lime, markerstrokecolor=:lime),
    "altdam"   => (label="Alt. damage", linestyle=:solid, colour=:darkgoldenrod4, markershape=:none, markercolor=:darkgoldenrod4, markerstrokecolor=:darkgoldenrod4),
    "parisext" => (label="Paris ext", linestyle=:dash, colour=:darkorange, markershape=:cross, markercolor=:darkorange, markerstrokecolor=:darkorange),
    "base"     => (label="Base", linestyle=:solid, colour=:yellow, markershape=:utriangle, markercolor=:yellow, markerstrokecolor=:yellow),
    "r5"       => (label="R = 5%", linestyle=:solid, colour=:darkblue, markershape=:diamond, markercolor=:darkblue, markerstrokecolor=:darkblue),
    "r4"       => (label="R = 4%", linestyle=:dash, colour=:blue, markershape=:diamond, markercolor=:blue, markerstrokecolor=:blue),
    "r3"       => (label="R = 3%", linestyle=:solid, colour=:deepskyblue4, markershape=:diamond, markercolor=:deepskyblue4, markerstrokecolor=:deepskyblue4),
    "r2"       => (label="R = 2%", linestyle=:dash, colour=:steelblue, markershape=:diamond, markercolor=:steelblue, markerstrokecolor=:steelblue),
    "r1"       => (label="R = 1%", linestyle=:solid, colour=:lightskyblue, markershape=:diamond, markercolor=:lightskyblue, markerstrokecolor=:lightskyblue)
)
# Run the scenarios and collect their results in the "results" dictionary
results = Dict([s => run_dice_scenario(s) for s in scenarios])
times  = results["cbopt"].times.+2020

******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
 Ipopt is released as open source code under the Eclipse Public License (EPL).
         For more information visit https://github.com/coin-or/Ipopt
******************************************************************************

Main results and comparison with the official GAMS version

For the variables ECO2, ppm, TATM, MIU, CPRICE and scc, we show the official values, the ones computed with DICEModel.jl and we plot these values.


ECO2 : CO₂ emissions [GtCO₂/yr]

Official GAMS outputs:

Show code
# Data from the DICE2023 manual
ECO2_gams = CSV.read(IOBuffer("""
scen      2020  2025  2050  2100
cbopt     42.9  42.9  37.1  15.9
t2c       42.9  42.9  27.2   1.2
t15c      42.9  13.1   5.7   0.0
altdam    42.9  42.7  20.9   0.0
parisext  42.9  43.3  44.4  42.3
base      42.9  44.9  54.6  75.7
r5        42.8  42.5  42.2  37.6
r4        44.1  43.9  39.3  28.9
r3        45.6  45.3  33.5  15.4
r2        46.8  46.7  22.5   0.0
r1        46.8  46.9  19.2   0.0
"""), DataFrame, delim=" ", ignorerepeated=true)
11×5 DataFrame
Rowscen2020202520502100
String15Float64Float64Float64Float64
1cbopt42.942.937.115.9
2t2c42.942.927.21.2
3t15c42.913.15.70.0
4altdam42.942.720.90.0
5parisext42.943.344.442.3
6base42.944.954.675.7
7r542.842.542.237.6
8r444.143.939.328.9
9r345.645.333.515.4
10r246.846.722.50.0
11r146.846.919.20.0

DICEModel.jl output

Show code
ECO2 = DataFrame([
    scenarios,
    [results[s].ECO2[1] for s in scenarios],
    [results[s].ECO2[2] for s in scenarios],
    [results[s].ECO2[7] for s in scenarios],
    [results[s].ECO2[17] for s in scenarios],
    ],
    ["scen","2020","2025","2050","2100"]
)
11×5 DataFrame
Rowscen2020202520502100
StringFloat64Float64Float64Float64
1cbopt42.941242.910337.055915.892
2t2c42.941242.856527.18271.20346
3t15c42.941213.065.66113-7.15383e-7
4altdam42.941242.684520.8841-9.29042e-7
5parisext42.941243.34944.367542.3349
6base42.882944.885854.628175.6928
7r542.750242.482842.200937.7465
8r444.077443.895939.347428.885
9r345.566745.34833.520215.4198
10r246.786346.677522.4688-1.0527e-6
11r147.115546.866619.1864-1.11658e-6

Differences:

Show code
ECO2_diff = copy(ECO2_gams);
ECO2_diff[:,2:end] .= ECO2_gams[:,2:end] .- ECO2[:,2:end]
11×5 DataFrame
Rowscen2020202520502100
String15Float64Float64Float64Float64
1cbopt-0.0411997-0.0103220.04408930.00799729
2t2c-0.04119970.04352930.0173196-0.00346045
3t15c-0.04119960.03997810.03887127.15383e-7
4altdam-0.04119970.01551680.01591179.29042e-7
5parisext-0.0411997-0.04904150.0325285-0.0348967
6base0.01712070.0142431-0.02808740.00719638
7r50.04978260.0172372-0.00086204-0.146482
8r40.02264380.00406249-0.04735630.0150089
9r30.0332938-0.0480203-0.0202132-0.019788
10r20.01365770.02249560.03119611.0527e-6
11r1-0.3155310.03340780.01361311.11658e-6

There are only two minor differences in r5 for 2100 and, above all, for r1 in 2020.

Plot:

Show code
scenarios_plot=["base","cbopt","parisext","t2c","r2","r1"]
p = plot()
for(i,s) in enumerate(scenarios_plot)
    if i == 1
        global p = plot(times[1:11] ,results[s].ECO2[1:11],ylim=(0,80), title="CO₂ emissions",ylabel="GtCO₂/yr";plot_attributes[s]...);
    else
        plot!(times[1:11] ,results[s].ECO2[1:11];plot_attributes[s]...)
    end
end
Example block output

ppm: CO₂ concentration [ppm]

Official GAMS outputs:

Show code
# Data from the DICE2023 manual
ppm_gams = CSV.read(IOBuffer("""
scen       2020  2025  2050  2100   2150
cbopt     416.2 429.9 487.8 569.2  497.9
t2c       416.2 429.9 474.7 474.7  437.9
altdam    416.2 429.8 466.7 458.5  401.0
parisext  416.2 430.1 501.3 652.5  763.5
base      416.2 430.9 517.7 774.9 1144.0
r5        416.2 429.7 495.7 635.5  671.6
r4        416.2 430.4 491.8 605.3  592.0
r3        416.2 431.2 484.0 555.1  494.2
r2        416.2 431.9 473.8 484.9  419.3
r1        416.2 432.0 473.3 449.5  389.3
"""), DataFrame, delim=" ", ignorerepeated=true)
10×6 DataFrame
Rowscen20202025205021002150
String15Float64Float64Float64Float64Float64
1cbopt416.2429.9487.8569.2497.9
2t2c416.2429.9474.7474.7437.9
3altdam416.2429.8466.7458.5401.0
4parisext416.2430.1501.3652.5763.5
5base416.2430.9517.7774.91144.0
6r5416.2429.7495.7635.5671.6
7r4416.2430.4491.8605.3592.0
8r3416.2431.2484.0555.1494.2
9r2416.2431.9473.8484.9419.3
10r1416.2432.0473.3449.5389.3

DICEModel.jl output

Show code
scenarios_ppm = ["cbopt","t2c","altdam","parisext","base","r5","r4","r3","r2","r1"]
ppm = DataFrame([
    scenarios_ppm,
    [results[s].ppm[1] for s in scenarios_ppm],
    [results[s].ppm[2] for s in scenarios_ppm],
    [results[s].ppm[7] for s in scenarios_ppm],
    [results[s].ppm[17] for s in scenarios_ppm],
    [results[s].ppm[27] for s in scenarios_ppm],
    ],
    ["scen","2020","2025","2050","2100","2150"]
)
10×6 DataFrame
Rowscen20202025205021002150
StringFloat64Float64Float64Float64Float64
1cbopt416.203429.915487.795569.243497.932
2t2c416.203429.888474.688474.749437.865
3altdam416.203429.802466.698458.507401.008
4parisext416.203430.134501.294652.492763.456
5base416.203430.898517.714774.931143.99
6r5416.203429.697495.734635.758673.822
7r4416.203430.433491.782605.346591.963
8r3416.203431.192484.031555.081494.245
9r2416.203431.884473.81484.93419.344
10r1416.203431.986473.289449.544389.345

Differences:

Show code
ppm_diff = copy(ppm_gams);
ppm_diff[:,2:end] .= ppm_gams[:,2:end] .- ppm[:,2:end]
10×6 DataFrame
Rowscen20202025205021002150
String15Float64Float64Float64Float64Float64
1cbopt-0.00319315-0.0148110.00507806-0.0425309-0.0324887
2t2c-0.003193150.01205420.0116026-0.04889290.0351935
3altdam-0.00319315-0.002141410.00183298-0.00691756-0.00801501
4parisext-0.00319315-0.03365830.006219090.007681850.0439192
5base-0.003193150.00156887-0.0142669-0.02963230.00881173
6r5-0.003193150.00286689-0.0343345-0.257502-2.22231
7r4-0.00319315-0.0326740.0177718-0.04580030.0366566
8r3-0.003193150.00825996-0.03069220.0188462-0.0451211
9r2-0.003193150.0163165-0.00950111-0.0300991-0.0435679
10r1-0.003193150.01420860.010667-0.0440142-0.0447197

Again, the only minor difference is for r5 in 2150.

Plot:

Show code
scenarios_plot=["base","cbopt","parisext","t2c","r2","r1"]
for(i,s) in enumerate(scenarios_plot)
    if i == 1
        global p = plot(times[1:17] ,results[s].ppm[1:17],ylim=(400,800), title="CO₂ concentrations",ylabel="ppm";plot_attributes[s]...);
    else
        plot!(times[1:17] ,results[s].ppm[1:17];plot_attributes[s]...)
    end
end
Example block output

TATM: Global Temperatures [°C diff since 1765]

Official GAMS outputs:

Show code
# Data from the DICE2023 manual
TATM_gams = CSV.read(IOBuffer("""
scen       2020  2025  2050  2100  2150
cbopt      1.25  1.42  1.92  2.58  2.29
t2c        1.25  1.42  1.85  2.00  1.86
altdam     1.25  1.42  1.81  1.89  1.58
parisext   1.25  1.43  2.01  3.00  3.61
base       1.25  1.43  2.10  3.55  4.91
r5         1.25  1.42  1.97  2.93  3.24
r4         1.25  1.43  1.95  2.77  2.84
r3         1.25  1.43  1.90  2.49  2.26
r2         1.25  1.43  1.84  2.07  1.73
r1         1.25  1.43  1.84  1.81  1.49
"""), DataFrame, delim=" ", ignorerepeated=true)
10×6 DataFrame
Rowscen20202025205021002150
String15Float64Float64Float64Float64Float64
1cbopt1.251.421.922.582.29
2t2c1.251.421.852.01.86
3altdam1.251.421.811.891.58
4parisext1.251.432.013.03.61
5base1.251.432.13.554.91
6r51.251.421.972.933.24
7r41.251.431.952.772.84
8r31.251.431.92.492.26
9r21.251.431.842.071.73
10r11.251.431.841.811.49

DICEModel.jl output

Show code
scenarios_TATM = ["cbopt","t2c","altdam","parisext","base","r5","r4","r3","r2","r1"]
TATM = DataFrame([
    scenarios_TATM,
    [results[s].TATM[1] for s in scenarios_TATM],
    [results[s].TATM[2] for s in scenarios_TATM],
    [results[s].TATM[7] for s in scenarios_TATM],
    [results[s].TATM[17] for s in scenarios_TATM],
    [results[s].TATM[27] for s in scenarios_TATM],
    ],
    ["scen","2020","2025","2050","2100","2150"]
)
10×6 DataFrame
Rowscen20202025205021002150
StringFloat64Float64Float64Float64Float64
1cbopt1.247151.424841.924412.580542.28903
2t2c1.247151.424731.850382.01.85951
3altdam1.247151.424371.806191.887231.57725
4parisext1.247151.425762.005443.002313.60536
5base1.247151.428932.098453.554924.91077
6r51.247151.423931.972612.927563.25007
7r41.247151.427011.945852.766062.84041
8r31.247151.430191.898572.489682.25982
9r21.247151.433081.844872.066041.72796
10r11.247151.433511.845041.814951.48749

Differences:

Show code
TATM_diff = copy(TATM_gams);
TATM_diff[:,2:end] .= TATM_gams[:,2:end] .- TATM[:,2:end]
10×6 DataFrame
Rowscen20202025205021002150
String15Float64Float64Float64Float64Float64
1cbopt0.002846-0.00484409-0.00440847-0.000542710.000974924
2t2c0.002846-0.00473142-0.000380345-1.95338e-80.000493873
3altdam0.002846-0.004371520.003806630.002773460.00274842
4parisext0.0028460.004238360.00456118-0.002307760.00464193
5base0.0028460.001074630.00154796-0.00491501-0.000773599
6r50.002846-0.00393098-0.002610550.0024408-0.0100711
7r40.0028460.002985450.004151070.00394109-0.000413549
8r30.002846-0.0001912280.001429580.0003240260.000182754
9r20.002846-0.00308213-0.004873680.00396350.00204289
10r10.002846-0.00350834-0.00504228-0.004946260.00251218

Again, the only minor difference is for r5 in 2150 (3.25 instead of 3.24)

Plot:

Show code
scenarios_plot=["base","cbopt","parisext","t2c","r4","r2"]
for(i,s) in enumerate(scenarios_plot)
    if i == 1
        global p = plot(times[1:17] ,results[s].TATM[1:17],ylim=(0.0,4.0), title="Global Temperatures",ylabel="°C from 1765";plot_attributes[s]...);
    else
        plot!(times[1:17] ,results[s].TATM[1:17];plot_attributes[s]...)
    end
end
Example block output

MIU: Emission control rate [%]

Official GAMS outputs:

Show code
# Data from the DICE2023 manual
MIU_gams = CSV.read(IOBuffer("""
scen    2020 2030 2040 2050 2060 2100
cbopt      5   24   31   39   46   84
t2c        5   24   42   55   69   99
altdam     5   24   48   65   76  100
parisext   5   13   21   27   33   57
base       5    6    8   10   12   22
r5         5   19   23   29   34   60
r4         5   24   29   36   42   70
r3         5   24   39   47   54   85
r2         5   24   48   66   73  100
r1         5   24   48   72   90  100
"""), DataFrame, delim=" ", ignorerepeated=true)
10×7 DataFrame
Rowscen202020302040205020602100
String15Int64Int64Int64Int64Int64Int64
1cbopt52431394684
2t2c52442556999
3altdam524486576100
4parisext51321273357
5base568101222
6r551923293460
7r452429364270
8r352439475485
9r2524486673100
10r1524487290100

DICEModel.jl output

Show code
scenarios_MIU = ["cbopt","t2c","altdam","parisext","base","r5","r4","r3","r2","r1"]
MIU = DataFrame([
    scenarios_MIU,
    [results[s].MIU[1] for s in scenarios_MIU] .* 100,
    [results[s].MIU[3] for s in scenarios_MIU] .* 100,
    [results[s].MIU[5] for s in scenarios_MIU] .* 100,
    [results[s].MIU[7] for s in scenarios_MIU] .* 100,
    [results[s].MIU[9] for s in scenarios_MIU] .* 100,
    [results[s].MIU[17] for s in scenarios_MIU] .* 100,
    ],
    ["scen","2020","2030","2040","2050","2060","2100"]
)
10×7 DataFrame
Rowscen202020302040205020602100
StringFloat64Float64Float64Float64Float64Float64
1cbopt5.024.030.992139.194446.4584.0037
2t2c5.024.041.80155.286969.261898.7874
3altdam5.024.048.065.310875.6007100.0
4parisext5.013.021.027.033.057.0
5base5.129026.370927.913529.8296411.541921.9397
6r55.018.496923.235128.965733.89559.8339
7r45.023.672229.299136.056441.701670.4212
8r35.024.039.383347.453853.834284.848
9r25.024.048.066.04572.5916100.0
10r15.024.048.072.090.0100.0

Differences:

Show code
MIU_diff = copy(MIU);
MIU_diff[:,2:end] .= MIU_gams[:,2:end] .- MIU[:,2:end]
10×7 DataFrame
Rowscen202020302040205020602100
StringFloat64Float64Float64Float64Float64Float64
1cbopt-8.70517e-72.86129e-70.00793158-0.194426-0.449981-0.00367471
2t2c-9.20874e-7-9.55094e-70.199016-0.286948-0.2618190.212592
3altdam-9.58301e-7-9.83517e-7-9.27644e-7-0.3107770.399331-9.45696e-7
4parisext-9.23627e-7-9.70824e-7-9.63689e-7-9.62881e-7-9.61297e-7-9.63202e-7
5base-0.129025-0.3709230.08647590.1703630.4581350.0603352
6r5-9.77287e-70.503117-0.2351480.03427680.1049780.16612
7r4-9.90236e-70.327775-0.299091-0.05635950.298353-0.421161
8r3-9.95254e-7-9.97168e-7-0.383298-0.4538290.1658170.152027
9r2-9.97388e-7-9.99252e-7-9.97949e-7-0.04502350.408357-9.88648e-7
10r1-9.98526e-7-9.99798e-7-9.99762e-7-9.99692e-7-9.99568e-7-9.99738e-7

No differences here !

Plot:

Show code
scenarios_plot=["cbopt","parisext","t2c","altdam"]
for(i,s) in enumerate(scenarios_plot)
    if i == 1
        global p = plot(times[1:17] ,results[s].MIU[1:17] .* 100,ylim=(0.0,100), title="Emission control rate",ylabel="%";plot_attributes[s]...);
    else
        plot!(times[1:17] ,results[s].MIU[1:17] .* 100;plot_attributes[s]...)
    end
end
Example block output

CPRICE: Carbon price [2019$ / tCO₂]

Plot:

Show code
scenarios_plot=["cbopt","parisext","t2c","r3"]
for(i,s) in enumerate(scenarios_plot)
    if i == 1
        global p = plot(times[1:9] ,results[s].CPRICE[1:9],ylim=(0,350), title="Carbon price",ylabel="2019\$ / tCO₂";plot_attributes[s]...);
    else
        plot!(times[1:9] ,results[s].CPRICE[1:9];plot_attributes[s]...)
    end
end
Example block output

scc: Social cost of carbon [2019$ / tCO₂]

Official GAMS outputs:

Show code
# Data from the DICE2023 manual
scc_gams = CSV.read(IOBuffer("""
scen       2020  2025  2050
cbopt        50    59   125
t2c          75    89   213
t15c       3557  4185 16552
altdam      124   146   281
parisext     61    72   159
base         66    78   175
r5           32    37    74
r4           49    58   107
r3           87   102   172
r2          176   207   302
r1          485   571   695
"""), DataFrame, delim=" ", ignorerepeated=true)
11×4 DataFrame
Rowscen202020252050
String15Int64Int64Int64
1cbopt5059125
2t2c7589213
3t15c3557418516552
4altdam124146281
5parisext6172159
6base6678175
7r5323774
8r44958107
9r387102172
10r2176207302
11r1485571695

DICEModel.jl output

Show code
scenarios_scc = ["cbopt","t2c","t15c", "altdam","parisext","base","r5","r4","r3","r2","r1"]
scc = DataFrame([
    scenarios_scc,
    [results[s].scc[1] for s in scenarios_scc],
    [results[s].scc[2] for s in scenarios_scc],
    [results[s].scc[7] for s in scenarios_scc],
    ],
    ["scen","2020","2025","2050"]
)
11×4 DataFrame
Rowscen202020252050
StringFloat64Float64Float64
1cbopt9.2193659.0669124.542
2t2c13.187789.034213.132
3t15c1773.014184.5616551.8
4altdam17.8033145.918281.084
5parisext15.952471.5199159.426
6base21.391577.6798175.028
7r55.6439437.282273.5004
8r410.147757.685106.929
9r318.5177101.816171.605
10r230.5636206.997302.131
11r146.4243570.813695.261

Differences:

Show code
scc_diff = copy(scc);
scc_diff[:,2:end] .= scc_gams[:,2:end] .- scc[:,2:end]
11×4 DataFrame
Rowscen202020252050
StringFloat64Float64Float64
1cbopt40.7806-0.06692420.457849
2t2c61.8123-0.034009-0.131736
3t15c1783.990.4440980.185102
4altdam106.1970.0819754-0.0837618
5parisext45.04760.480074-0.425736
6base44.60850.320193-0.0284349
7r526.3561-0.2821590.499588
8r438.85230.3150.0711168
9r368.48230.184180.394926
10r2145.4360.0027403-0.130938
11r1438.5760.187235-0.26097

This is the only part that still needs to be checked, as there are significant differences for the base year (2020). For the other years DICEModel.jl provides identical results (up to the approximation of the data published) than the official GAMS version.

Plot:

Show code
scenarios_plot=["base","cbopt","t2c","altdam","r4","r2"]
for(i,s) in enumerate(scenarios_plot)
    if i == 1
        global p = plot(times[1:7] ,results[s].scc[1:7],ylim=(0,400), title="Social cost of carbon",ylabel="2019\$ / tCO₂";plot_attributes[s]...);
    else
        plot!(times[1:7] ,results[s].scc[1:7];plot_attributes[s]...)
    end
end
Example block output

Detailed model output

The whole model output (variables and post-processing computed values) can be retrieved in the Excel file below:

Show code
keys(results["cbopt"])
out_vars = [v for v in keys(results["cbopt"]) if typeof(results["cbopt"][v]) <: Vector{<: Number}]

all_results = DataFrame(
    scenario = String[],
    variable = String[],
    year     = Int64[],
    value    = Float64[],
)

for s in scenarios, v in out_vars, ti in 1:length(times)
    push!(all_results,[s,string(v),times[ti],results[s][v][ti]])
end
all_results = unstack(all_results,"year","value")

XLSX.writetable("DICEModelDetailedResults.xlsx",
all_results=(collect(DataFrames.eachcol(all_results)),DataFrames.names(all_results))
)

DICEModelDetailedResults.xlsx