$TITLE Bridging the gap using energy services: Demonstrating a general framework for linking top-down and bottom-up models $ontext Copenhagen, March, 2018 Kristoffer S. Andersen (krisan@dtu.dk) Lars B. Termansen (lbt@ens.dk) This code illustrate the decomposition strategies discussed in section 2 of the paper "Bridging the gap using energy services: Demonstrating a general framework for linking top-down and bottom-up models", link "https://doi.org/10.1016/j.energy.2018.11.153". The GAMS code replicates the results presented in section 2 of the paper. The code defining the: a) Bottom-up stand-a-lone model b) Top-down stand-a-lone model c) Integrated approach d) Decomposition strategy: Soft-linking using full information approach e) Decomposition strategy: Soft-linking using partial information and average cost pricing approach $offtext * ================================================================================================================================ * Defining set and parameter used throughtout the model * ================================================================================================================================ SETS * Model loop definition scen "Scenarios" / s00*s01 / iter "Soft-linking iteration" / i01*i05 / * Methodology definitions methodologies "Different modelling methodologies" /bottom_up "Bottom-up stand-alone", integrated "Integrated top-down bottom-up", full_softlink "Soft-linking using full information", part_softlink "Soft-linking using partial information and average cost pricing" / * Defining subset for raporting purposes method(methodologies); method(methodologies) = NO; PARAMETERS * Parameters for reporting of results solution(methodologies,scen,*) "Reporting of solution across scenarios" iterlog(methodologies,scen,iter,*) "Reporting of solution from soft-linking across scenarios and iterations" OPTIONS solution:2:2:1; OPTIONS iterlog:4:1:1; * ================================================================================================================================ * Bottom-up model stand-alone * ================================================================================================================================ * Set definitions for bottom-up model SETS i "Technologies" / biomassboilr, oilboilr, heatpump / j "Seasons" / winter, summer /; SCALAR numeria "Size of numeria used in the CGE model" /1/; PARAMETERS * Data for bottom-up energy model cki(i) "Capital input for heat service production (million euro/MW)" / biomassboilr 1.200, oilboilr 0.75, heatpump 1.25 / cxi(i) "Cost of fuel input for heat service production (euro/MWh)" / biomassboilr 0.000220, oilboilr 0.000208, heatpump 0.000120 / dkj(j) "Capacity demand by season (MW)" / summer 2.5, winter 5 / hj(j) "Hours demand by season" / summer 3000, winter 5000 / * Parameters for data transfer between BU and TD models pX_td "TopDown price index of output" / 1 / pK_td "TopDown price index of capital" / 1 / qY_td "TopDown quantity index of energy service" / 1 / pY_bu "BottomUp price index of energy service" / 1 / qK_bu "BottomUp capital in energy sector" / 5 / qK_td "BottomUp capital in energy sector" / 5 / qX_bu "BottomUp inputs in energy sector" / 5 / qX_td "BottomUp inputs in energy sector" / 5 / pY_bu0 "Benchmark annual consumption weighted shadow price of heat service from bottom-up model" ; * =============================================================================== * * Bottom-up stand-alone: LP cost minization problem * * =============================================================================== * VARIABLES COST "Dual objective - total heat service cost (euro)"; POSITIVE VARIABLES xij(i,j) "Fuel input to heat service production technology (MWh/year)" ki(i) "Installed capacity of technology i (MW)"; EQUATIONS OBJ1 "Cost of energy system (euro)" DEMAND1(j) "Seasonal fuel/demand constraint (MWh/season)" CAPACITY1(i,j) "Seasonal technology capacity constraint (MW)"; OBJ1.. COST =e= Px_td * SUM((i,j), cxi(i)*xij(i,j))+ Pk_td*SUM(i,cki(i)*ki(i)); DEMAND1(j).. SUM(i, xij(i,j)) =g= dkj(j)*hj(j)*qY_td; CAPACITY1(i,j).. ki(i)*hj(j) =g= xij(i,j); MODEL BU_PRIMAL / OBJ1, DEMAND1, CAPACITY1 /; SOLVE BU_PRIMAL USING LP MINIMIZING cost; * =============================================================================== * * Bottom-up stand-alone: LP profit maximization problem * * =============================================================================== * VARIABLE PROFIT "Primal objective - profit from heat service production"; POSITIVE VARIABLES lambda(j) "Shadow price of seasonal demand of heat service" mu(i,j) "Shadow price of capacity of technology i in season j" EQUATIONS OBJ2 "Energy system profit" DEMAND2(i,j) "Optimal fuel constraint" CAPACITY2(i) "Optimal capacity constraint"; OBJ2.. PROFIT =e= sum(j, lambda(j)*dkj(j)*hj(j))*qY_td; DEMAND2(i,j).. Px_td * cxi(i) + mu(i,j) =g= lambda(j); CAPACITY2(i).. Pk_td * cki(i) =g= sum(j, mu(i,j)*hj(j)); *Solve LP primal using information from LP dual; *lambda.l(j) = DEMAND1.M(j); *mu.l(i,j) = CAPACITY1.M(i,j); MODEL BU_DUAL / OBJ2, DEMAND2, CAPACITY2 /; solve BU_DUAL using LP MAXIMIZING profit; * Set benchmark seasonal demand weighted shadow price of heat service pY_bu0 = sum(j, Demand1.m(j)*dkj(j)*hj(j))/sum(j,dkj(j)*hj(j)); * ================================================================================================================================ * Top-down model: Standard CGE formulation * ================================================================================================================================ TABLE sam Base year social accounts X EN RA X 100 -5 -95 K -95 -5 100 * ------------------------------------ E -5 10 -5 POSITIVE VARIABLES X "Activity level for sector X (Rest-of-economy)" Y "Activity level for sector Y (Energy-service-production)" W "Activity level for sector W (Utility)" PX "Price index for commodity X" PY "Price index for commodity Y" PK "Price index for primary factor K" PW "Price index for welfare (expenditure function)" RA "Income definition for representative household"; EQUATIONS PRF_X "Zero profit for sector X" PRF_Y "Zero profit for sector Y" PRF_W "Zero profit for sector W" MKT_X "Supply-demand balance for commodity X" MKT_Y "Supply-demand balance for commodity Y" MKT_K "Supply-demand balance for primary factor K" MKT_W "Supply-demand balance for aggregate demand" I_RA "Income definition for representative household"; * Zero profit conditions; PRF_X.. 100 * PK**(19/20) * PY**(1/20) =G= 100 * PX; PRF_Y.. 5 * PK + 5 * PX =G= 10 * PY; PRF_W.. 100 * PX**(19/20) * PY**(1/20) =G= 100 * PW; * Market clearing conditions: MKT_X.. 100* X =G= 95*W*PX**(19/20) * PY**(1 /20)/PX + 5*Y; MKT_Y.. 10 * Y =G= 5*W*PX**(19/20) * PY**(1 /20)/PY + 5*X*PK**(19/20) * PY**(1/20)/PY; MKT_K.. 100 =G= 95*X*PY**(1 /20) * PK**(19/20)/PK + 5*Y; MKT_W.. 100* W =E= RA / PW; * Income constraints: I_RA.. RA =E= 100 * PK; MODEL TOPDOWN_STANDARD /PRF_X.X, PRF_W.W, PRF_Y.Y, MKT_X.PX, MKT_Y.PY, MKT_K.PK, MKT_W.PW, I_RA.RA/; PK.L=numeria; PX.L=numeria; PY.L=numeria; PW.L = numeria; W.L=1; X.L=1; Y.L=1; RA.L=100; *Setting numeria as either capital or good X or private consumption good PW.FX = numeria ; *PK.FX = numeria ; *PX.FX = numeria ; *Baseline calibration check TOPDOWN_STANDARD.ITERLIM = 0; SOLVE TOPDOWN_STANDARD USING MCP; TOPDOWN_STANDARD.ITERLIM = 10000000; * ================================================================================================================================ * Top-down model: Integrated LP CGE formulation * ================================================================================================================================ *In order to build the hybrid model we integrate the Karush-Kuhn-Tucker condition from the LP problem into *the top-down model, adjusting the equations so that the previous exogenous parameter qY_td, Pk_td *and Px_td are replaced by the corresponding variables from the top-down model EQUATIONS MKT_X_INTEGRATED "Supply-demand balance for commodity X" MKT_K_INTEGRATED "Supply-demand balance for primary factor K" TD_DEMAND1(j) "Top-down: Seasonal fuel/demand constraint (MWh/season)" TD_CAPACITY1(i,j) "Top-down: Seasonal technology capacity constraint (MW)" TD_DEMAND2(i,j) "Top-down: Optimal fuel constraint" TD_CAPACITY2(i) "Top-down: Optimal capacity constraint" PRF_Y_INTEGRATED "Motified zero profit for sector Y (annual heat service production)"; *Modified zero profit condition PRF_Y_INTEGRATED.. sum(j,dkj(j)*hj(j)*lambda(j)) =g= 10*PY; *Additional zero profit condition from bottom-up model TD_DEMAND2(i,j).. PX * cxi(i) + mu(i,j) =g= lambda(j); TD_CAPACITY2(i).. PK * cki(i) =g= sum(j, mu(i,j)*hj(j)); *Modified zero profit condition MKT_X_INTEGRATED.. 100*X =g= 95*W*PX**(19/20)*PY**( 1/20)/PX + sum((i,j),cxi(i)*xij(i,j)); MKT_K_INTEGRATED.. 100 =g= 95*X*PY**(1 /20)*PK**(19/20)/PK + sum( i ,cki(i)*ki(i)); *Additional zero profit condition from bottom-up model TD_DEMAND1(j).. SUM(i, xij(i,j)) =g= dkj(j)*hj(j)*Y ; TD_CAPACITY1(i,j).. ki(i)*hj(j) =g= xij(i,j) ; MODEL TOPDOWN_INTEGRATED /PRF_X.X, PRF_W.W, MKT_Y.PY,MKT_W.PW,I_RA.RA, *Addtional and modified constraints relative to standard CGE formulation PRF_Y_INTEGRATED.Y, TD_DEMAND2.xij, TD_CAPACITY2.ki, MKT_X_INTEGRATED.PX, MKT_K_INTEGRATED.PK, TD_DEMAND1.lambda, TD_CAPACITY1.mu/; *Baseline calibration check TOPDOWN_INTEGRATED.ITERLIM = 0; SOLVE TOPDOWN_INTEGRATED USING MCP; TOPDOWN_INTEGRATED.ITERLIM = 10000000; * ================================================================================================================================ * Soft-linked with full information * ================================================================================================================================ *Adding connection points to top-down model VARIABLES tau "Endogenous tax to match energy service price from bottom-up model" tau_rev "Revenue from endogneous tax "; EQUATIONS PRF_Y_SOFTLINK "Motified zero profit for sector Y (annual heat service production)" MKT_K_SOFTLINK_FULL "Modified market clearance condition clearanance for capital demand" MKT_X_SOFTLINK_FULL "Modified market clearance condition clearanance for commodity X" Eq_BU_PY "Bottom-up heat service price" Eq_Tau_Rev "Revenue from tau"; *Moditifed zero-profit condition PRF_Y_SOFTLINK.. 10 * ((qX_bu+qK_bu)/(10*qY_td)) * ((qK_bu/(qX_bu+qK_bu))*PK + (qX_bu/(qX_bu+qK_bu))* PX) =G= 10 * PY *(1-tau); *Moditifed market clearance zero-profit condition MKT_K_SOFTLINK_FULL.. 100 =G= 95*X*PY**(1 /20) * PK**(19/20)/PK + qK_BU; MKT_X_SOFTLINK_FULL.. 100 * X =G= 95*W*PX**(19/20) * PY**(1 /20)/PX + qX_BU; *Additional constraint to determine price in bottom-up model Eq_BU_PY.. PY =E= PY_BU; *Additional constraint to determine price in bottom-up model Eq_Tau_rev.. tau_rev =E= tau * PY * Y * 10 ; MODEL TOPDOWN_SOFTLINKFULL /PRF_X.X, PRF_W.W, MKT_Y.PY, MKT_W.PW, I_RA.RA, *Addtional and modified constraints relative to standard CGE formulation PRF_Y_SOFTLINK.Y, MKT_X_SOFTLINK_FULL.PX MKT_K_SOFTLINK_FULL.PK Eq_BU_PY.tau, Eq_Tau_rev.tau_rev/; *Baseline calibration check TOPDOWN_SOFTLINKFULL.ITERLIM = 0; SOLVE TOPDOWN_SOFTLINKFULL USING MCP; TOPDOWN_SOFTLINKFULL.ITERLIM = 10000000; * ================================================================================================================================ * Soft-linked using partial information and average cost pricing * ================================================================================================================================ EQUATIONS MKT_K_SOFTLINK_PART "Modified market clearance condition clearanance for capital demand"; *Modified market clearance condition clearanance for capital demand MKT_K_SOFTLINK_PART.. 100 =G= 95*X*PY**(1 /20) * PK**(19/20)/PK + qK_BU + tau_rev / PK; MODEL TOPDOWN_SOFTLINKPART /PRF_X.X, PRF_W.W, MKT_Y.PY, MKT_W.PW, I_RA.RA, *Addtional and modified constraints relative to standard CGE formulation PRF_Y_SOFTLINK.Y, MKT_X_SOFTLINK_FULL.PX MKT_K_SOFTLINK_PART.PK Eq_BU_PY.tau, Eq_Tau_rev.tau_rev/; *Baseline calibration check TOPDOWN_SOFTLINKPART.ITERLIM = 0; SOLVE TOPDOWN_SOFTLINKPART USING MCP; TOPDOWN_SOFTLINKPART.ITERLIM = 10000000; * ================================================================================================================================ * Introduce lower bounds (test) * ================================================================================================================================ *xij.lo(i,j) = 0; *xij.lo("biomassboilr","winter") = 2500; * ================================================================================================================================ * Starting policy experiment section * ================================================================================================================================ * Policy experiment: Bottom-up stand-a-lone * ================================================================================================================================ method("bottom_up")=YES; *Reset capital cost of oilboiler ahead of new scenrio loop cki("oilboilr") = 0.75; LOOP(scen, * We introduce a prohibitive tax on oilboilers to simulate a climate mitigation policy aimed at baning this technology cki("oilboilr")$(ord(scen) gt 1) = cki("oilboilr")*2.0; *Reset overall model parameters ahead of each iteration loop pX_td = numeria; pK_td = numeria; * Solve bottom-up stand-alone SOLVE BU_PRIMAL USING LP MINIMIZING COST; * Save results related to the bottom-up model properties solution(method,scen,"Heat pump output (MWh)") = SUM(j, xij.l("heatpump",j)); solution(method,scen,"Oil boiler output (MWh)") = SUM(j, xij.l("oilboilr",j)); solution(method,scen,"Biomass boiler output (MWh)") = SUM(j, xij.l("biomassboilr",j)); solution(method,scen,"Heat pump capacity (MW)") = ki.l("heatpump"); solution(method,scen,"Oil boiler capacity (MW)") = ki.l("oilboilr"); solution(method,scen,"Biomass boiler capacity (MW)") = ki.l("biomassboilr"); solution(method,scen,"Capital input in heat service production") = sum(i,(cki(i)*ki.l(i))); solution(method,scen,"Fuel input in heat service production") = sum((i,j),(cxi(i)*xij.l(i,j))); solution(method,scen,"Total value of heat service (million euro)") = COST.l; solution(method,scen,"Marginal annual heat service price euro/Mwh") = SUM(j, 1000000*DEMAND1.m(j)*hj(j)*dkj(j))/ SUM(j, hj(j)*dkj(j)); solution(method,scen,"Average annual heat service price euro/Mwh") = 1000000*COST.l / (sum(j,dkj(j)*hj(j))); solution(method,scen,"Value of capacity to heat service (million euro)") = Pk_td*SUM(i, cki(i)*ki.l(i)); solution(method,scen,"Value of fuel to heat service (million euro)") = Px_td*SUM((i,j), cxi(i)*xij.l(i,j)); ); method(methodologies) = NO; * ================================================================================================================================ * Loop for scenarios iterations : Integrated model * ================================================================================================================================ method("integrated")=YES; *Reset capital cost of oilboiler ahead of new scenrio loop cki("oilboilr") = 0.75; *Reset bottom-up benchmark solution pX_td = 1; pK_td = 1; qY_td = 1; pY_bu = 1; SOLVE BU_PRIMAL USING LP MINIMIZING COST; LOOP(scen, * We introduce a prohibitive tax on oilboilers to simulate a climate mitigation policy aimed at baning this technology * The tax doubles the capital cost of oil boilers cki("oilboilr")$(ord(scen) gt 1) = cki("oilboilr")*2.0; *Reset overall model parameters ahead of each iteration loop PK.L=numeria; PX.L=numeria; PY.L=numeria; PW.L = numeria; W.L=1; X.L=1;Y.L=1;RA.L=100; * Solve Integrated model SOLVE TOPDOWN_INTEGRATED USING MCP; * Save results related to the bottom-up model properties solution(method,scen,"Heat pump output (MWh)") = SUM(j, xij.l("heatpump",j)); solution(method,scen,"Oil boiler output (MWh)") = SUM(j, xij.l("oilboilr",j)); solution(method,scen,"Biomass boiler output (MWh)") = SUM(j, xij.l("biomassboilr",j)); solution(method,scen,"Heat pump capacity (MW)") = ki.l("heatpump"); solution(method,scen,"Oil boiler capacity (MW)") = ki.l("oilboilr"); solution(method,scen,"Biomass boiler capacity (MW)") = ki.l("biomassboilr"); solution(method,scen,"Capital input in heat service production") = sum(i,(cki(i)*ki.l(i))); solution(method,scen,"Capacity cost of heat service (million euro)") = PK.l*SUM(i, cki(i)*ki.l(i)); solution(method,scen,"Fuel input in heat service production") = sum((i,j),(cxi(i)*xij.l(i,j))); solution(method,scen,"Marginal annual heat service price euro/Mwh") = SUM(j, 1000000*TD_DEMAND1.m(j)*hj(j)*dkj(j)) / SUM(j, hj(j)*dkj(j)); solution(method,scen,"Average annual heat service price euro/Mwh") = 1000000* ( Px.l * SUM((i,j), cxi(i)*xij.l(i,j))+ Pk.l*SUM(i,cki(i)*ki.l(i))) / sum(j,dkj(j)*hj(j)*Y.L); * Save results related to top-down model properties solution(method,scen,"Price level of heat service") = PY.l ; solution(method,scen,"Price level of good X") = PX.l ; solution(method,scen,"Price level of capital" ) = PK.l ; solution(method,scen,"Output of X sector") = X.l * 100 ; solution(method,scen,"Output of Y sector (heat service)") = Y.l * 10 ; solution(method,scen,"Final demand") = RA.l ; solution(method,scen,"Final demand, X") = 95*W.l*PX.l**(19/20) * PY.l**(1 /20)/PX.l; solution(method,scen,"Final demand, Y") = 5* W.l*PX.l**(19/20) * PY.l**(1 /20)/PY.l; solution(method,scen,"Welfare change in percent (EV)") = 100 * (W.L - 1) ; * Save results directly related to the linking of the top-down and bottom-up model solution(method,scen,"Value of fuel to heat service (million euro)") = PX.L*SUM((i,j), cxi(i)*xij.l(i,j)); solution(method,scen,"Value of capacity to heat service (million euro)") = PK.L*SUM(i, cki(i)*ki.l(i)); solution(method,scen,"Total value of heat service (million euro)") = PY.L*Y.L*10; solution(method,scen,"Total value of goods production (million euro)") = PX.L*X.L*100; ); method(methodologies) = NO; * ================================================================================================================================ * Loop for scenarios and iterations for softlink top-down and botom-up model exchanging full information * ================================================================================================================================ method("full_softlink")=YES; *Reset capital cost of oilboiler ahead of new scenrio loop cki("oilboilr") = 0.75; *Reset bottom-up benchmark solution pX_td = 1; pK_td = 1; qY_td = 1; pY_bu = 1; SOLVE BU_PRIMAL USING LP MINIMIZING COST; LOOP(scen, * We introduce a prohibitive tax on oilboilers to simulate a climate mitigation policy aimed at baning this technology * The tax doubles the capital cost of oil boilers cki("oilboilr")$(ord(scen) gt 1) = cki("oilboilr")*2.0; *Reset overall model parameters ahead of each iteration loop PK.L=numeria; PX.L=numeria; PY.L=numeria; PW.L = numeria; W.L=1; X.L=1;Y.L=1;RA.L=100; LOOP(iter, * Update prices for input and quantity for output in bottom-up model qY_td = Y.L; pX_td = PX.l; pK_td = PK.l; SOLVE BU_PRIMAL USING LP MINIMIZING COST; * Update quantities of input and prices for output in top-down model qX_bu = SUM((i,j), cxi(i)*xij.l(i,j)); qK_bu = SUM(i, cki(i)*ki.l(i)) ; pY_bu = sum(j, demand1.m(j)*dkj(j)*hj(j))/sum(j,dkj(j)*hj(j)) / pY_bu0; pY_bu = sum(j, demand1.m(j)*(dkj(j)*hj(j) - sum(i, xij.lo(i,j))))/sum(j,dkj(j)*hj(j)- sum(i, xij.lo(i,j))) / pY_bu0; PY.l = pY_bu; SOLVE TOPDOWN_SOFTLINKFULL USING MCP; * Iteration rapporting iterlog(method,scen,iter,"Price level of heat service (BU)") = pY_bu; iterlog(method,scen,iter,"Fuel cost of heat service (million euro)") = pX_td*qX_bu; iterlog(method,scen,iter,"Capacity cost of heat service (million euro)") = pK_td*qK_bu; iterlog(method,scen,iter,"Heat pump output (MWh)") = SUM(j, xij.l("heatpump",j)); iterlog(method,scen,iter,"Oil boiler output (MWh)") = SUM(j, xij.l("oilboilr",j)); iterlog(method,scen,iter,"Biomass boiler output (MWh)") = SUM(j, xij.l("biomassboilr",j)); iterlog(method,scen,iter,"Total cost of heat service (BU)") = COST.l; iterlog(method,scen,iter,"ShadowPrice") = SUM(j, 1000000*DEMAND1.M(j)*hj(j)*dkj(j)) / SUM(j, hj(j)*dkj(j)); iterlog(method,scen,iter,"Total value of heat serivce (TD)") = Y.l * pY_bu * 10 ; iterlog(method,scen,iter,"Output of Y sector (heat service)") = Y.l*10; iterLog(method,scen,iter,"Wedge Rate") = tau.l; iterLog(method,scen,iter,"Price level of heat service (TD)") = PY.l; iterLog(method,scen,iter,"Price level of good capital") = PK.l; iterLog(method,scen,iter,"Price level of X sector") = PX.l; iterLog(method,scen,iter,"Welfare change in percent (EV)") = 100 * (W.L - 1); iterLog(method,scen,iter,"Value of capacity to heat service (million euro)") = qK_bu * PK.L ; iterLog(method,scen,iter,"Value of fuel to heat service (million euro)") = qX_bu * PX.L ; iterLog(method,scen,iter,"Value of Wedge Rent (million euro)") = tau_rev.l; ); * Save results related to the bottom-up model properties solution(method,scen,"Heat pump output (MWh)") = SUM(j, xij.l("heatpump",j)); solution(method,scen,"Oil boiler output (MWh)") = SUM(j, xij.l("oilboilr",j)); solution(method,scen,"Biomass boiler output (MWh)") = SUM(j, xij.l("biomassboilr",j)); solution(method,scen,"Heat pump capacity (MW)") = ki.l("heatpump"); solution(method,scen,"Oil boiler capacity (MW)") = ki.l("oilboilr"); solution(method,scen,"Biomass boiler capacity (MW)") = ki.l("biomassboilr"); solution(method,scen,"Capital input in heat service production") = sum(i,(cki(i)*ki.l(i))); solution(method,scen,"Capacity cost of heat service (million euro)") = Pk_td*SUM(i, cki(i)*ki.l(i)); solution(method,scen,"Fuel input in heat service production") = sum((i,j),(cxi(i)*xij.l(i,j))); solution(method,scen,"Marginal annual heat service price euro/Mwh") = SUM(j, 1000000*DEMAND1.m(j)*hj(j)*dkj(j)) / SUM(j, hj(j)*dkj(j)); solution(method,scen,"Average annual heat service price euro/Mwh") = 1000000*COST.l / (sum(j,dkj(j)*hj(j))* Y.l); * solution(method,scen,"Average annual heat service price euro/Mwh") = 1000000 * ( pX_td * SUM((i,j), cxi(i)*xij.l(i,j))+ pK_td*SUM(i,cki(i)*ki.l(i))) / sum(j,dkj(j)*hj(j)*qY_td); * Save results related to top-down model properties solution(method,scen,"Price level of heat service") = PY.l ; solution(method,scen,"Price level of good X") = PX.l ; solution(method,scen,"Price level of capital" ) = PK.l ; solution(method,scen,"Output of X sector") = X.l * 100 ; solution(method,scen,"Output of Y sector (heat service)") = Y.l * 10 ; solution(method,scen,"Final demand") = RA.l ; solution(method,scen,"Final demand, X") = 95*W.l*PX.l**(19/20) * PY.l**(1 /20)/PX.l; solution(method,scen,"Final demand, Y") = 5* W.l*PX.l**(19/20) * PY.l**(1 /20)/PY.l; solution(method,scen,"Welfare change in percent (EV)") = 100 * (W.L - 1); * Save results directly related to the linking of the top-down and bottom-up model solution(method,scen,"Value of capacity to heat service (million euro)") = qK_bu * PK.L ; solution(method,scen,"Value of fuel to heat service (million euro)") = qX_bu * PX.L ; solution(method,scen,"Value of Wedge Rent (million euro)") = tau_rev.l; *Add wedge calculation solution(method,scen,"Total value of heat service (million euro)") = pY_bu*Y.L*10; solution(method,scen,"Total value of goods production (million euro)") = PX.L*X.L*100; ); method(methodologies) = NO; * ================================================================================================================================ * Loop for scenarios and iterations for softlink top-down and botom-up model exchanging partial information * ================================================================================================================================ method("part_softlink")=YES; *Reset capital cost of oilboiler ahead of new scenrio loop cki("oilboilr") = 0.75; *Reset bottom-up benchmark solution pX_td = 1; pK_td = 1; qY_td = 1; pY_bu = 1; SOLVE BU_PRIMAL USING LP MINIMIZING COST; LOOP(scen, * We introduce a prohibitive tax on oilboilers to simulate a climate mitigation policy aimed at baning this technology * The tax doubles the capital cost of oil boilers cki("oilboilr")$(ord(scen) gt 1) = cki("oilboilr")*2.0; *Reset overall model parameters ahead of each iteration loop PK.L=numeria; PX.L=numeria; PY.L=numeria; PW.L = numeria; W.L=1; X.L=1;Y.L=1;RA.L=100; LOOP(iter, * Update prices for input and quantity for output in bottom-up model qY_td = Y.L; pX_td = PX.l; pK_td = PK.l; * pK_td = 1; SOLVE BU_PRIMAL USING LP MINIMIZING COST; * Update quantities of input and prices for output in top-down model qX_bu = SUM((i,j), cxi(i)*xij.l(i,j)); * ================================================================================================================================ * We now skip updating the capital demand quantities from the bottom-up model to illustrate the partial linking approach * qK_bu = SUM(i, cki(i)*ki.l(i)) ; qK_bu = 5; * =============================================================================================================================== * We define the price of heat service as the average cost of heat service production in the bottom-up model pY_bu = COST.l/ (10*Y.l); PY.L = pY_bu; SOLVE TOPDOWN_SOFTLINKPART USING MCP; * Iteration rapporting iterlog(method,scen,iter,"Price level of heat service (BU)") = pY_bu; iterlog(method,scen,iter,"Fuel cost of heat service (million euro)") = pX_td*qX_bu; iterlog(method,scen,iter,"Capacity cost of heat service (million euro)") = pK_td*qK_bu; iterlog(method,scen,iter,"Heat pump output (MWh)") = SUM(j, xij.l("heatpump",j)); iterlog(method,scen,iter,"Oil boiler output (MWh)") = SUM(j, xij.l("oilboilr",j)); iterlog(method,scen,iter,"Biomass boiler output (MWh)") = SUM(j, xij.l("biomassboilr",j)); iterlog(method,scen,iter,"Total cost of heat service (BU)") = COST.l; iterlog(method,scen,iter,"ShadowPrice") = SUM(j, 1000000*DEMAND1.M(j)*hj(j)*dkj(j)) / SUM(j, hj(j)*dkj(j)); iterlog(method,scen,iter,"Total value of heat serivce (TD)") = Y.l * pY_bu * 10 ; iterlog(method,scen,iter,"Output of Y sector (heat service)") = Y.l*10; iterLog(method,scen,iter,"Wedge Rate") = tau.l; iterLog(method,scen,iter,"Price level of heat service (TD)") = PY.l; iterLog(method,scen,iter,"Price level of good capital") = PK.l; iterLog(method,scen,iter,"Price level of X sector") = PX.l; iterLog(method,scen,iter,"Welfare change in percent (EV)") = 100 * (W.L - 1); iterLog(method,scen,iter,"Value of capacity to heat service (million euro)") = qK_bu * PK.L ; iterLog(method,scen,iter,"Value of fuel to heat service (million euro)") = qX_bu * PX.L ; iterLog(method,scen,iter,"Value of Wedge Rent (million euro)") = tau_rev.l; ); * Save results related to the bottom-up model properties solution(method,scen,"Heat pump output (MWh)") = SUM(j, xij.l("heatpump",j)); solution(method,scen,"Oil boiler output (MWh)") = SUM(j, xij.l("oilboilr",j)); solution(method,scen,"Biomass boiler output (MWh)") = SUM(j, xij.l("biomassboilr",j)); solution(method,scen,"Heat pump capacity (MW)") = ki.l("heatpump"); solution(method,scen,"Oil boiler capacity (MW)") = ki.l("oilboilr"); solution(method,scen,"Biomass boiler capacity (MW)") = ki.l("biomassboilr"); solution(method,scen,"Capital input in heat service production") = sum(i,(cki(i)*ki.l(i))); solution(method,scen,"Capacity cost of heat service (million euro)") = Pk_td*SUM(i, cki(i)*ki.l(i)); solution(method,scen,"Fuel input in heat service production") = sum((i,j),(cxi(i)*xij.l(i,j))); solution(method,scen,"Marginal annual heat service price euro/Mwh") = SUM(j, 1000000*DEMAND1.m(j)*hj(j)*dkj(j)) / SUM(j, hj(j)*dkj(j)); * solution(method,scen,"Average annual heat service price euro/Mwh") = 1000000*COST.l / (sum(j,dkj(j)*hj(j))* Y.l); solution(method,scen,"Average annual heat service price euro/Mwh") = 1000000 * ( pX_td * SUM((i,j), cxi(i)*xij.l(i,j))+ pK_td*SUM(i,cki(i)*ki.l(i))) / sum(j,dkj(j)*hj(j)*qY_td); * Save results related to top-down model properties solution(method,scen,"Price level of heat service") = PY.l ; solution(method,scen,"Price level of good X") = PX.l ; solution(method,scen,"Price level of capital" ) = PK.l ; solution(method,scen,"Output of X sector") = X.l * 100 ; solution(method,scen,"Output of Y sector (heat service)") = Y.l * 10 ; solution(method,scen,"Final demand") = RA.l ; solution(method,scen,"Final demand, X") = 95*W.l*PX.l**(19/20) * PY.l**(1 /20)/PX.l; solution(method,scen,"Final demand, Y") = 5* W.l*PX.l**(19/20) * PY.l**(1 /20)/PY.l; solution(method,scen,"Welfare change in percent (EV)") = 100 * (W.L - 1); * Save results directly related to the linking of the top-down and bottom-up model solution(method,scen,"Value of capacity to heat service (million euro)") = qK_bu * PK.L ; solution(method,scen,"Value of fuel to heat service (million euro)") = qX_bu * PX.L ; solution(method,scen,"Value of Wedge Rent (million euro)") = tau_rev.l; *Add wedge calculation solution(method,scen,"Total value of heat service (million euro)") = pY_bu*Y.L*10; solution(method,scen,"Total value of goods production (million euro)") = PX.L*X.L*100; ); method(methodologies) = NO; display solution, iterlog; ***Option to generate a excel output file prepared for pivot *execute_unload "results.gdx" solution iterlog *execute 'gdxxrw.exe results.gdx par=solution rng=solution!a2 dim=3 rdim=3 cdim=0' *execute 'gdxxrw.exe results.gdx par=iterlog rng=iterlog!a2 dim=4 rdim=4 cdim=0'