diff --git a/message_ix/model/MESSAGE/data_load.gms b/message_ix/model/MESSAGE/data_load.gms index f5848720d..fedc452ce 100644 --- a/message_ix/model/MESSAGE/data_load.gms +++ b/message_ix/model/MESSAGE/data_load.gms @@ -97,6 +97,9 @@ Set rating_unrated(rating) ; rating_unrated(rating) = yes ; rating_unrated('unrated') = no ; +* this is a temporary data for learning technologies. this need to be made automatic +Set newtec(tec) / wind_ppl / ; + *----------------------------------------------------------------------------------------------------------------------* * assignment and computation of MESSAGE-specific auxiliary parameters * *----------------------------------------------------------------------------------------------------------------------* diff --git a/message_ix/model/MESSAGE/model_learningeos.gms b/message_ix/model/MESSAGE/model_learningeos.gms new file mode 100644 index 000000000..a1705b4c1 --- /dev/null +++ b/message_ix/model/MESSAGE/model_learningeos.gms @@ -0,0 +1,68 @@ +Sets + size 'size' / small, medium, large / ; + +Alias (size,size2); +Parameters + cap_new2(node,newtec,year_all2) 'annual newly installed capacity' + rho(newtec) 'economy of scale parameter' + / wind_ppl 1 / #0.8 + b(newtec) 'technology cost learning parameter' + / wind_ppl 0.9 / #0.9 + u(size) 'unit size' + / small 5 + medium 10 + large 50 / + inv_cost0(node,newtec) 'initial capex' + n_unit0(newtec) 'initial number of unit' + / wind_ppl 100 / + u0(newtec) 'reference size' + / wind_ppl 5 / ; +inv_cost0(node,newtec)=1500; + +scalar hist_length the length of historical periods; +hist_length = card(year_all2) - card(model_horizon); + +*Table +* cap_new2(node,newtec,year_all2) 'annual newly installed capacity' +* 690 700 710 720 +* Westeros.wind_ppl 100 10 0 0 ; + +Variables + N_unit(node,newtec,size,year_all2) number of units for each size every year + CapexTec(node,newtec,year_all2) capital cost in dollar per kW + Object objective function ; + +Positive Variables + N_unit; + +Equations + Objective_inner total investment cost + C_balance installed capacity balance + Capex_estimate estimating average capex + Annual_investment annual investment cost +; + + +Objective_inner.. Object =e= sum((node,newtec,year_all2), CapexTec(node,newtec,year_all2)*cap_new2(node,newtec,year_all2)) ; +C_balance(node,newtec,year_all2).. sum(size, N_unit(node,newtec,size,year_all2)*u(size)) =e= cap_new2(node,newtec,year_all2) ; +Capex_estimate(node,newtec,year_all2).. CapexTec(node,newtec,year_all2)*cap_new2(node,newtec,year_all2) =g= sum(size,inv_cost0(node,newtec) + *N_unit(node,newtec,size,year_all2)*u(size) + *[(((sum((size2,year_all3)$(ord(year_all3) le ord(year_all2) and ord(year_all3) gt hist_length), N_unit(node,newtec,size2,year_all3))+n_unit0(newtec))/n_unit0(newtec))**(-b(newtec)))] + *[((u(size)/u0(newtec))**rho(newtec))]) ; +Annual_investment(node,newtec,year_all2).. sum(year_all3$(ord(year_all3) le ord(year_all2) and ord(year_all3) gt hist_length), CapexTec(node,newtec,year_all3)*cap_new2(node,newtec,year_all3)) + - sum(year_all3$(ord(year_all3) le (ord(year_all2)-1) and ord(year_all3) gt hist_length), CapexTec(node,newtec,year_all3)*cap_new2(node,newtec,year_all3)) =g= 0 ; + +model leaningeos / all /; + +$ontext + +Objective_inner.. Object =e= sum((node,newtec,year_all2), CapexTec(node,newtec,year_all2)*cap_new2(node,newtec,year_all2)) ; +C_balance(node,newtec,year_all2).. sum(size, N_unit(node,newtec,size,year_all2)*u(size)) =e= cap_new2(node,newtec,year_all2) ; +Capex_estimate(node,newtec,year_all2).. CapexTec(node,newtec,year_all2)*cap_new2(node,newtec,year_all2) =g= sum(size,inv_cost0(node,newtec) + *N_unit(node,newtec,size,year_all2)*u(size) + *[(((sum((size2,year_all3)$(ord(year_all3) le ord(year_all2) and ord(year_all3) gt hist_length), N_unit(node,newtec,size2,year_all3))+n_unit0(newtec))/n_unit0(newtec))**(-b(newtec)))] + *[((u(size)/u0(newtec))**rho(newtec))]) ; + +model leaningeos / all /; + +$offtext \ No newline at end of file diff --git a/message_ix/model/MESSAGE/model_setup.gms b/message_ix/model/MESSAGE/model_setup.gms index 74a27742f..d467c34b9 100644 --- a/message_ix/model/MESSAGE/model_setup.gms +++ b/message_ix/model/MESSAGE/model_setup.gms @@ -84,3 +84,4 @@ $INCLUDE MESSAGE/scaling_investment_costs.gms *----------------------------------------------------------------------------------------------------------------------* $INCLUDE MESSAGE/model_core.gms +$INCLUDE MESSAGE/model_learningeos.gms diff --git a/message_ix/model/MESSAGE/model_solve.gms b/message_ix/model/MESSAGE/model_solve.gms index b6ae12891..2749b9fe0 100644 --- a/message_ix/model/MESSAGE/model_solve.gms +++ b/message_ix/model/MESSAGE/model_solve.gms @@ -64,6 +64,81 @@ EMISSION_CONSTRAINT.m(node,type_emission,type_tec,type_year)$( %AUX_BOUNDS% AUX_ACT_BOUND_UP(node,tec,year_all,year_all2,mode,time)$( ACT.l(node,tec,year_all,year_all2,mode,time) > 0 AND %AUX_BOUNDS% ACT.l(node,tec,year_all,year_all2,mode,time) = %AUX_BOUND_VALUE% ) = yes ; + + +elseif %foresight% = -1, +*** +* Recursive-dynamic and myopic model +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* For the myopic and rolling-horizon models, loop over horizons and iteratively solve the model, keeping the decision +* variables from prior periods fixed. +* This option is selected by setting the GAMS global variable ``%foresight%`` to a value greater than 0, +* where the value represents the number of years that the model instance is considering when iterating over the periods +* of the optimization horizon. +* +* Loop over :math:`\hat{y} \in Y`, solving +* +* .. math:: +* \min_x \ OBJ = \sum_{y \in \hat{Y}(\hat{y})} OBJ_y(x_y) \\ +* \text{s.t. } x_{y'} = x_{y'}^* \quad \forall \ y' < y +* +* where :math:`\hat{Y}(\hat{y}) = \{y \in Y | \ |\hat{y}| - |y| < optimization\_horizon \}` and +* :math:`x_{y'}^*` is the optimal value of :math:`x_{y'}` in iteration :math:`|y'|` of the iterative loop. +* +* The advantage of this implementation is that there is no need to 'store' the optimal values of all decision +* variables in additional reporting parameters - the last model solve automatically includes the results over the +* entire model horizon and can be imported via the ixmp interface. +*** + year(year_all) = no ; + year(model_horizon) = yes ; + LOOP(year_all$( model_horizon(year_all) ), + +* year(model_horizon) = yes ; + year4(year_all2)$((ord(year_all2) < ord(year_all))) = yes ; +* + option threads = 4 ; + Solve MESSAGE_LP using LP minimizing OBJ ; +* write model status summary + status('perfect_foresight','modelstat') = MESSAGE_LP.modelstat ; + status('perfect_foresight','solvestat') = MESSAGE_LP.solvestat ; + status('perfect_foresight','resUsd') = MESSAGE_LP.resUsd ; + status('perfect_foresight','objEst') = MESSAGE_LP.objEst ; + status('perfect_foresight','objVal') = MESSAGE_LP.objVal ; + +* write an error message if model did not solve to optimality + IF( NOT ( MESSAGE_LP.modelstat = 1 OR MESSAGE_LP.modelstat = 8 ), + put_utility 'log' /'+++ MESSAGEix did not solve to optimality - run is aborted, no output produced! +++ ' ; + ABORT "MESSAGEix did not solve to optimality!" + ) ; + +* passing CAP_NEW values to update cap_new2 data for unit and size optimization + cap_new2(node,newtec,year_all2) = CAP_NEW.l(node,newtec,year_all2); + display cap_new2; + + solve leaningeos using nlp minimizing Object; +* passing CapexTec values to update inv_cost data for MESSAGE optimization + inv_cost(node,newtec,year_all) = CapexTec.l(node,newtec,year_all); + + Execute_unload 'learningeos.gdx',N_unit,CapexTec,cap_new2, CAP_NEW.l, cap_new2;; + +* fix all variables of the current iteration period 'year_all' to the optimal levels + EXT.fx(node,commodity,grade,year4) = EXT.l(node,commodity,grade,year4) ; + CAP_NEW.fx(node,tec,year4) = CAP_NEW.l(node,tec,year4) ; + CAP.fx(node,tec,year4,year4) = CAP.l(node,tec,year4,year4) ; + ACT.fx(node,tec,year4,year4,mode,time) = ACT.l(node,tec,year4,year4,mode,time) ; + CAP_NEW_UP.fx(node,tec,year4) = CAP_NEW_UP.l(node,tec,year4) ; + CAP_NEW_LO.fx(node,tec,year4) = CAP_NEW_LO.l(node,tec,year4) ; + ACT_UP.fx(node,tec,year4,time) = ACT_UP.l(node,tec,year4,time) ; + ACT_LO.fx(node,tec,year4,time) = ACT_LO.l(node,tec,year4,time) ; + + + Display year,year4,year_all,model_horizon,CapexTec.l,hist_length ; + ) ; # end of the recursive-dynamic loop for technology cost learning and economy of scale module + + + + + else *** * Recursive-dynamic and myopic model @@ -93,41 +168,53 @@ else LOOP(year_all$( model_horizon(year_all) ), * include all past periods and future periods including the period where the %foresight% is reached - year(year_all) = yes ; +* year(year_all) = yes ; + year(year_all2)$( ORD(year_all2) < (ORD(year_all) + %foresight%) ) = yes ; + year4(year_all2)$((ord(year_all2) < ord(year_all))) = yes ; * reset the investment cost scaling parameter - year(year_all2)$( ORD(year_all2) > ORD(year_all) - AND duration_period_sum(year_all,year_all2) < %foresight% ) = yes ; +* year(year_all2)$( ORD(year_all2) > ORD(year_all) +* AND duration_period_sum(year_all,year_all2) < %foresight% ) = yes ; -* write a status update and time elapsed to the log file, solve the model - put_utility 'log' /'+++ Solve the recursive-dynamic version of MESSAGEix - iteration ' year_all.tl:0 ' +++ ' ; - $$INCLUDE includes/aux_computation_time.gms - Solve MESSAGE_LP using LP minimizing OBJ ; -* write model status summary - status(year_all,'modelstat') = MESSAGE_LP.modelstat ; - status(year_all,'solvestat') = MESSAGE_LP.solvestat ; - status(year_all,'resUsd') = MESSAGE_LP.resUsd ; - status(year_all,'objEst') = MESSAGE_LP.objEst ; - status(year_all,'objVal') = MESSAGE_LP.objVal ; - -* write an error message AND ABORT THE SOLVE LOOP if model did not solve to optimality - IF( NOT ( MESSAGE_LP.modelstat = 1 OR MESSAGE_LP.modelstat = 8 ), - put_utility 'log' /'+++ MESSAGEix did not solve to optimality - run is aborted, no output produced! +++ ' ; - ABORT "MESSAGEix did not solve to optimality!" - ) ; + option threads = 4 ; + Solve MESSAGE_LP using LP minimizing OBJ ; +* write model status summary + status('perfect_foresight','modelstat') = MESSAGE_LP.modelstat ; + status('perfect_foresight','solvestat') = MESSAGE_LP.solvestat ; + status('perfect_foresight','resUsd') = MESSAGE_LP.resUsd ; + status('perfect_foresight','objEst') = MESSAGE_LP.objEst ; + status('perfect_foresight','objVal') = MESSAGE_LP.objVal ; + +* write an error message if model did not solve to optimality + IF( NOT ( MESSAGE_LP.modelstat = 1 OR MESSAGE_LP.modelstat = 8 ), + put_utility 'log' /'+++ MESSAGEix did not solve to optimality - run is aborted, no output produced! +++ ' ; + ABORT "MESSAGEix did not solve to optimality!" + ) ; + + IF(%learningmode% = 1, +* passing CAP_NEW values to update cap_new2 data for unit and size optimization + cap_new2(node,newtec,year_all2) = CAP_NEW.l(node,newtec,year_all2); + display cap_new2; + solve leaningeos using nlp minimizing Object; +* passing CapexTec values to update inv_cost data for MESSAGE optimization + inv_cost(node,newtec,year_all) = CapexTec.l(node,newtec,year_all); + + Execute_unload 'learningeos.gdx',N_unit,CapexTec,cap_new2, CAP_NEW.l, cap_new2;; + ); * fix all variables of the current iteration period 'year_all' to the optimal levels - EXT.fx(node,commodity,grade,year_all) = EXT.l(node,commodity,grade,year_all) ; - CAP_NEW.fx(node,tec,year_all) = CAP_NEW.l(node,tec,year_all) ; - CAP.fx(node,tec,year_all2,year_all)$( map_period(year_all2,year_all) ) = CAP.l(node,tec,year_all,year_all2) ; - ACT.fx(node,tec,year_all2,year_all,mode,time)$( map_period(year_all2,year_all) ) - = ACT.l(node,tec,year_all2,year_all,mode,time) ; - CAP_NEW_UP.fx(node,tec,year_all) = CAP_NEW_UP.l(node,tec,year_all) ; - CAP_NEW_LO.fx(node,tec,year_all) = CAP_NEW_LO.l(node,tec,year_all) ; - ACT_UP.fx(node,tec,year_all,time) = ACT_UP.l(node,tec,year_all,time) ; - ACT_LO.fx(node,tec,year_all,time) = ACT_LO.l(node,tec,year_all,time) ; + EXT.fx(node,commodity,grade,year4) = EXT.l(node,commodity,grade,year4) ; + CAP_NEW.fx(node,tec,year4) = CAP_NEW.l(node,tec,year4) ; + CAP.fx(node,tec,year4,year4) = CAP.l(node,tec,year4,year4) ; + ACT.fx(node,tec,year4,year4,mode,time) = ACT.l(node,tec,year4,year4,mode,time) ; + CAP_NEW_UP.fx(node,tec,year4) = CAP_NEW_UP.l(node,tec,year4) ; + CAP_NEW_LO.fx(node,tec,year4) = CAP_NEW_LO.l(node,tec,year4) ; + ACT_UP.fx(node,tec,year4,time) = ACT_UP.l(node,tec,year4,time) ; + ACT_LO.fx(node,tec,year4,time) = ACT_LO.l(node,tec,year4,time) ; + + Display year,year4,year_all,model_horizon,CapexTec.l,hist_length ; ) ; # end of the recursive-dynamic loop ) ; # end of if statement for the selection betwen perfect-foresight or recursive-dynamic model diff --git a/message_ix/model/MESSAGE/sets_maps_def.gms b/message_ix/model/MESSAGE/sets_maps_def.gms index 1c0a5ea69..1d2ba641c 100644 --- a/message_ix/model/MESSAGE/sets_maps_def.gms +++ b/message_ix/model/MESSAGE/sets_maps_def.gms @@ -140,24 +140,26 @@ $ONEMPTY *** Sets - node world - regions - countries - grid cells - commodity resources - electricity - water - land availability - etc. - level levels of the reference energy system or supply chain ( primary - secondary - ... - useful ) - sector sectors (for integration with MACRO) - grade grades of extraction of raw materials - tec technologies - mode modes of operation - emission greenhouse gases - pollutants - etc. - land_scenario scenarios of land use (for land-use model emulator) - land_type types of land use - year_all years (over entire model horizon) - year (year_all) years included in a model instance (for myopic or rolling-horizon optimization) - time subannual time periods (seasons - days - hours) - shares share constraint relations - relation generic linear relations - lvl_spatial hierarchical levels of spatial resolution - lvl_temporal hierarchical levels of temporal resolution - rating identifies the 'quality' of the renewable energy potential (bins acc. to Sullivan) + node world - regions - countries - grid cells + commodity resources - electricity - water - land availability - etc. + level levels of the reference energy system or supply chain ( primary - secondary - ... - useful ) + sector sectors (for integration with MACRO) + grade grades of extraction of raw materials + tec technologies + mode modes of operation + emission greenhouse gases - pollutants - etc. + land_scenario scenarios of land use (for land-use model emulator) + land_type types of land use + year_all years (over entire model horizon) + year (year_all) years included in a model instance (for myopic or rolling-horizon optimization) + year4 (year_all) years included in a model instance (for myopic or rolling-horizon optimization) + year_hist (year_all) historical year + time subannual time periods (seasons - days - hours) + shares share constraint relations + relation generic linear relations + lvl_spatial hierarchical levels of spatial resolution + lvl_temporal hierarchical levels of temporal resolution + rating identifies the 'quality' of the renewable energy potential (bins acc. to Sullivan) ; * definition of aliases diff --git a/message_ix/model/MESSAGE_master.gms b/message_ix/model/MESSAGE_master.gms index bd6bb99fe..33fe2dfa2 100644 --- a/message_ix/model/MESSAGE_master.gms +++ b/message_ix/model/MESSAGE_master.gms @@ -58,6 +58,11 @@ $SETGLOBAL macromode "none" * rolling horizon (period-by-period, recursive-dynamic with limited foresight - 'number of years of foresight' $SETGLOBAL foresight "0" +** for recursive dynamic approach, this is to activate/deactivate technology learning module ** +* deactivate technology learning - 0 +* activate technology learning - 1 +$SETGLOBAL learningmode "0" + ** add a comment and name extension for model report files (e.g. run-specific info, calibration notes) - optional ** $SETGLOBAL comment ""