diff --git a/modules/47_regipol/module.gms b/modules/47_regipol/module.gms index 781e7685b..ea2c85774 100644 --- a/modules/47_regipol/module.gms +++ b/modules/47_regipol/module.gms @@ -34,6 +34,22 @@ *' These bounds either serve to align REMIND with historic and near-term data in specific regions or represent regional policies (like national coal phase-out plans) that can be activated via switches. *' Finally, there are some regional adjustments in the datainput file that modify input data for specific regions for cases which have not been generalized for all REMIND regions yet. *' +*' ## Glossary of Carbon Price Rescaling Terms +*' +*' * **Target Deviation**: The fractional difference between current iteration emissions and the specified emissions target, normalized by a reference year's emissions. The algorithm drives this deviation toward zero (within a specified tolerance band) by adjusting the carbon tax. +*' * **Rescale Factor**: The multiplicative factor applied to the regional carbon tax at the end of each outer (Nash) iteration. A value of 1.0 means no change; > 1 raises the tax; < 1 lowers it. +*' * **Rescale Slope**: The estimated marginal sensitivity of regional emissions to the carbon tax in a given emission market (change in emissions / change in price). Expected to be negative (higher price leads to lower emissions). Computed from the current iteration and a baseline reference iteration. +*' * **Slope Reference Iteration**: The baseline outer iteration used to compute the Rescale Slope. It is reset when the target changes, when the slope window is exceeded, or when a degenerate slope is detected to ensure the computed slope reflects the local abatement cost curve. +*' * **Slope Window**: The maximum allowed number of iterations between the current outer iteration and the Slope Reference Iteration. If the reference becomes older than this threshold, it is forced to reset. +*' * **SquareDev Fallback**: A robust fallback method that bypasses the Rescale Slope entirely and computes the tax rescale factor quadratically as `(1 + target_deviation)^2`. Used when reliable slope information is unavailable or degenerate. +*' * **Degenerate Slope**: A condition where the change in emissions (slope numerator) is negligibly small relative to the reference year's emissions (e.g., in near-net-zero scenarios). Triggers a SquareDev Fallback and resets the reference iteration. +*' * **Positive Slope**: An anomalous scenario where the computed Rescale Slope is positive, falsely implying that a higher carbon tax increased emissions. Addressed by either repeating a valid historical slope or triggering a SquareDev Fallback. +*' * **Slope Clamp**: Hard bounds applied to the Rescale Slope before it is used. Prevents a single iteration from making an extreme price adjustment due to noisy slope estimates. +*' * **Upper Clamp Bound**: The maximum (least-negative) permitted value for the Rescale Slope (default: -0.3). Prevents the algorithm from over-adjusting when the slope is too flat. +*' * **Lower Clamp Bound**: The minimum (most-negative) permitted value for the Rescale Slope (default: -5). Prevents extreme tax jumps when the slope is too steep. +*' * **Adaptive Upper Clamp Bound**: An extension of the Upper Clamp Bound that halves the limit when the clamp triggers in consecutive outer iterations, indicating persistent flat-slope behavior. Resets to the default when the clamp is no longer triggered. +*' * **Oscillation Dampener**: A safeguard that fires when the Rescale Factor alternates between > 1 and < 1 for three consecutive outer iterations. When triggered, the factor's adjustment distance from 1.0 is halved to dampen volatility. +*' *' @authors Renato Rodrigues, Felix Schreyer *###################### R SECTION START (MODULETYPES) ########################## diff --git a/modules/47_regipol/regiCarbonPrice/datainput.gms b/modules/47_regipol/regiCarbonPrice/datainput.gms index 358d7f883..76272b447 100644 --- a/modules/47_regipol/regiCarbonPrice/datainput.gms +++ b/modules/47_regipol/regiCarbonPrice/datainput.gms @@ -19,10 +19,15 @@ pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt) = 0; ***-------------------------------------------------- $IFTHEN.emiMkt not "%cm_emiMktTarget%" == "off" +*** slope calculation algorithm parameters +s47_slopeDegenerateThreshold = 1e-2; !! minimum emission change (fraction of 2005 emissions) for a reliable slope numerator +s47_slopeMaxWindow = 5; !! maximum iterations between reference and current; beyond this the reference is reset to stay local on the abatement cost curve + *** Auxiliar parameters based on emission targets information loop((ttot,ttot2,ext_regi,emiMktExt,target_type_47,emi_type_47)$pm_emiMktTarget(ttot,ttot2,ext_regi,emiMktExt,target_type_47,emi_type_47), !!calculated sets that depends on data parameter regiEmiMktTarget(ext_regi) = yes; !! assigning values to set containing extended regions that have regional emission targets - regiANDperiodEmiMktTarget_47(ttot2,ext_regi) = yes; !! assigning values to set containing extended regions and terminal years of regional emission targets + regiANDperiodEmiMktTarget_47(ttot2,ext_regi) = yes; !! assigning values to set containing extended regions and terminal years of regional emission targets + p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) = -0.3; !! initialize upper clamp bound for rescale slope ); *** Calculating set containing regions that should be controlled by a given regional emission target. @@ -106,6 +111,8 @@ p47_taxemiMkt_init(ttot,regi,emiMkt)$(p47_taxCO2eq_ref(ttot,regi) and (NOT(p47_t p47_factorRescaleSlope_iter("1","2020","2030",ext_regi,emiMktExt) = 0; *** initialize required parameter for cm_emiMktTarget rescale oscillation dampening. p47_factorRescaleemiMktCO2Tax_iter("1","2020","2030",ext_regi,emiMktExt) = 0; +*** initialize required parameter to track if the upper slope clamp fired. +p47_upperClampActive_iter("1","2020","2030",ext_regi,emiMktExt) = 0; $ENDIF.emiMkt diff --git a/modules/47_regipol/regiCarbonPrice/declarations.gms b/modules/47_regipol/regiCarbonPrice/declarations.gms index 071a9337b..e207f832d 100644 --- a/modules/47_regipol/regiCarbonPrice/declarations.gms +++ b/modules/47_regipol/regiCarbonPrice/declarations.gms @@ -13,6 +13,8 @@ Parameter s47_firstFreeYear "value of first free year for the carbon price trajectory" s47_prefreeYear "value of the last non-free year for the carbon price trajectory" + s47_slopeDegenerateThreshold "minimum emission change percentage relative to 2005 emissions required for a reliable slope; below this the slope numerator is treated as degenerate [fraction]" + s47_slopeMaxWindow "maximum number of iterations between the reference iteration and the current iteration for slope calculation; beyond this the reference is reset to force a local slope [#]" pm_emiLULUCF_GrassiShift(ttot,all_regi) "difference between Magpie land-use change emissions and UNFCCC emissions in 2015 to correct for national accounting in emissions targets [GtC]" pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt) "deviation of emissions of current iteration from target emissions, for budget target this is the difference normalized by target emissions, while for year targets this is the difference normalized by 2005 emissions [%]" pm_taxemiMkt(ttot,all_regi,all_emiMkt) "CO2 tax path per region and emissions market [T$/GtC]" @@ -62,6 +64,9 @@ $endif.emiMktTargetType p47_factorRescaleemiMktCO2Tax_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) "parameter to save rescale factor across iterations for debugging purposes [%]" p47_clampedRescaleSlope_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) "auxiliary parameter to save the slope value before clamping. Useful for debugging purposes [#]" p47_dampedFactorRescaleemiMktCO2Tax_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) "auxiliary parameter to save the rescale factor value before dampening. Useful for debugging purposes [#]" + p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) "adaptive upper clamp bound for rescale slope; starts at -0.3 and halves after two consecutive triggers at the same level [#]" + p47_slopeUpperClampBound_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) "adaptive upper clamp bound per iteration for debugging [#]" + p47_upperClampActive_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) "1 if the upper slope clamp fired in this iteration, 0 otherwise [0 or 1]" *** Parameters necessary to define the CO2 tax curve shape p47_targetConverged(ttot,ext_regi) "boolean to store if emission target has converged [0 or 1]" diff --git a/modules/47_regipol/regiCarbonPrice/postsolve.gms b/modules/47_regipol/regiCarbonPrice/postsolve.gms index 961f596b0..46f32e176 100644 --- a/modules/47_regipol/regiCarbonPrice/postsolve.gms +++ b/modules/47_regipol/regiCarbonPrice/postsolve.gms @@ -142,7 +142,7 @@ p47_emiTargetMkt_iter(iteration,ttot,regi, emiMktExt,emi_type_47) = p47_emiTarge *** Emission markets (EU Emission trading system and Effort Sharing) ***-------------------------------------------------- -$IFTHEN.emiMkt not "%cm_emiMktTarget%" == "off" +$IFTHEN.emiMkt not "%cm_emiMktTarget%" == "off" *** Removing economy wide co2 tax parameters for regions within the emiMKt controlled targets (this is necessary here to remove any calculation made in other modules after the last run in the postsolve) loop(ext_regi$regiEmiMktTarget(ext_regi), @@ -251,6 +251,8 @@ loop((ext_regi,ttot)$regiANDperiodEmiMktTarget_47(ttot,ext_regi), p47_slopeReferenceIteration_iter(iteration,ttot,ext_regi) = 1; elseif(NOT(p47_currentConvergence_iter(iteration,ttot,ext_regi) eq p47_currentConvergence_iter(iteration-1,ttot,ext_regi))), !! reset the iteration reference for slope calculation if the target that is being analyzed changes p47_slopeReferenceIteration_iter(iteration,ttot,ext_regi) = ord(iteration); + elseif((ord(iteration) - p47_slopeReferenceIteration_iter(iteration-1,ttot,ext_regi)) gt s47_slopeMaxWindow), !! reset if reference is too old to represent the local abatement cost curve + p47_slopeReferenceIteration_iter(iteration,ttot,ext_regi) = ord(iteration); else p47_slopeReferenceIteration_iter(iteration,ttot,ext_regi) = p47_slopeReferenceIteration_iter(iteration-1,ttot,ext_regi); ); @@ -259,6 +261,7 @@ loop((ext_regi,ttot)$regiANDperiodEmiMktTarget_47(ttot,ext_regi), *** resetting rescale factor for the next iteration p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = 0; pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = 0; +regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,rescaleType) = NO; *** Calculating the emissions tax rescale factor based on previous iterations emission reduction for current targets loop(ext_regi$regiEmiMktTarget(ext_regi), loop((ttot2)$(ttot2.val eq p47_currentConvergencePeriod(ext_regi)), @@ -267,7 +270,7 @@ loop(ext_regi$regiEmiMktTarget(ext_regi), loop(emiMkt$emiMktGroup(emiMktExt,emiMkt), loop(regi$regiEmiMktTarget2regi_47(ext_regi,regi), *** if rescale factor was already calculated for ext_regi, there is no need to recalculate it - continue$(pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt)); + continue$(sum(rescaleType$regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,rescaleType), 1)); *** calculating the rescale factor loop(iteration2$((iteration2.val le iteration.val) and (iteration2.val eq p47_slopeReferenceIteration_iter(iteration,ttot2,ext_regi))), !!reference iteration for slope calculation *** if it is the first iteration or the reference iteration changed, initialize the rescale factor based on remaining deviation @@ -297,52 +300,98 @@ loop(ext_regi$regiEmiMktTarget(ext_regi), (pm_taxemiMkt_iteration(iteration,ttot2,regi,emiMkt) - pm_taxemiMkt_iteration(iteration2,ttot2,regi,emiMkt)); *** else if denominator in relation to first iteration is not close to zero, calculate the price slope in relation to the first iteration mitigation and price levels instead elseif(NOT(abs(pm_taxemiMkt_iteration(iteration,ttot2,regi,emiMkt) - pm_taxemiMkt_iteration("1",ttot2,regi,emiMkt)) lt 1e-2)), - regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"slope_firstIteration") = YES; - p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = - (p47_emiMktCurrent_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) - p47_emiMktCurrent_iter("1",ttot,ttot2,ext_regi,emiMktExt)) - / - (pm_taxemiMkt_iteration(iteration,ttot2,regi,emiMkt) - pm_taxemiMkt_iteration("1",ttot2,regi,emiMkt)); -*** else if there is a previous iteration calculated slope, repeat the previous iteration slope - elseif((iteration.val gt 1) and (p47_slopeReferenceIteration_iter(iteration,ttot,ext_regi) - p47_slopeReferenceIteration_iter(iteration-1,ttot,ext_regi) eq 0)), - regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"slope_repeatPrev") = YES; - p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = p47_factorRescaleSlope_iter(iteration-1,ttot,ttot2,ext_regi,emiMktExt); -*** else slope is not available, set the rescale factor based on remaining deviation - else - regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_noSlope") = YES; - pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = power(1+pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt), 2); +*** only use iteration 1 as reference if it falls within the slope window; otherwise iteration 1 is too old +*** to represent the local abatement cost and squareDev is more appropriate + if((iteration.val - 1) le s47_slopeMaxWindow, + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"slope_firstIteration") = YES; + p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = + (p47_emiMktCurrent_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) - p47_emiMktCurrent_iter("1",ttot,ttot2,ext_regi,emiMktExt)) + / + (pm_taxemiMkt_iteration(iteration,ttot2,regi,emiMkt) - pm_taxemiMkt_iteration("1",ttot2,regi,emiMkt)); + else + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_outsideWindow") = YES; + pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = power(1+pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt), 2); + ); ); -*** if we are using the slope - if(NOT(regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_noSlope")), -*** if the slope is positive - if(p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) gt 0, - regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,rescaleType) = NO; -*** if there is a previous iteration calculated slope, repeat the previous iteration slope to avoid the positive value because we assume a trade-off between tax and emission levels - if((iteration.val gt 1) and (p47_slopeReferenceIteration_iter(iteration,ttot,ext_regi) - p47_slopeReferenceIteration_iter(iteration-1,ttot,ext_regi) eq 0), - regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"slope_repeatPrev_positiveSlope") = YES; - p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = p47_factorRescaleSlope_iter(iteration-1,ttot,ttot2,ext_regi,emiMktExt); -*** else slope is not available, set the rescale factor based on remaining deviation - else - regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_noNonPositiveSlope") = YES; - pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = power(1+pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt), 2); - ); +*** if the slope is positive + if(p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) gt 0, + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,rescaleType) = NO; +*** if there is a previous iteration calculated slope, repeat the previous iteration slope to avoid the positive value because we assume a trade-off between tax and emission levels + if((iteration.val gt 1) and (p47_slopeReferenceIteration_iter(iteration,ttot2,ext_regi) - p47_slopeReferenceIteration_iter(iteration-1,ttot2,ext_regi) eq 0) and (p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt)), + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"slope_repeatPrev_positiveSlope") = YES; + p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = p47_factorRescaleSlope_iter(iteration-1,ttot,ttot2,ext_regi,emiMktExt); +*** else slope is not available, set the rescale factor based on remaining deviation + else + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_noNonPositiveSlope") = YES; + pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = power(1+pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt), 2); ); -*** if we are still using the slope - if(NOT(regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_noNonPositiveSlope")), -*** clamp slopes values to avoid extreme changes (or no change) on a single iteration (avoid corner cases where other parts of the model changes causing undesirable fluctuations on the calculated slope) - if((p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) gt -0.3) OR (p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) lt -5), + ); +*** if we are still using the slope + if(NOT(regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_noNonPositiveSlope")) + AND NOT(regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_outsideWindow")), +*** initialize or reset adaptive upper clamp bound when reference iteration changes + if((p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) eq 0) + OR ((iteration.val gt 1) AND (p47_slopeReferenceIteration_iter(iteration,ttot2,ext_regi) ne p47_slopeReferenceIteration_iter(iteration-1,ttot2,ext_regi))), + p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) = -0.3; + ); + p47_slopeUpperClampBound_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) = p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt); +*** if upper clamp fires for second consecutive time at minimum level (-0.075), immediately override to squareDev and reset + if((p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) gt p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt)) + AND (p47_upperClampActive_iter(iteration-1,ttot,ttot2,ext_regi,emiMktExt) eq 1) + AND (p47_slopeUpperClampBound_iter(iteration-1,ttot,ttot2,ext_regi,emiMktExt) eq p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt)) + AND (p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) lt -0.07), + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,rescaleType) = NO; + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_adaptiveClamp") = YES; + pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = power(1+pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt), 2); + p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) = -0.3; + else +*** clamp slopes values to avoid extreme changes (or no change) on a single iteration + if((p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) gt p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt)) OR (p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) lt -5), p47_clampedRescaleSlope_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) = p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt); ); - p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = max(-5,min(-0.3, p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt))); -*** calculate the tax rescale factor using the above calculated slope - pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = + p47_upperClampActive_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) = (p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) gt p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt)); + p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) = max(-5, min(p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt), p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt))); +*** calculate the tax rescale factor using the above calculated slope + pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = ( (pm_emiMktTarget(ttot,ttot2,ext_regi,emiMktExt,target_type_47,emi_type_47) - p47_emiMktCurrent_iter(iteration,ttot,ttot2,ext_regi,emiMktExt)) - / + / (p47_factorRescaleSlope(ttot,ttot2,ext_regi,emiMktExt) * pm_taxemiMkt_iteration(iteration,ttot2,regi,emiMkt)) ) + 1; +*** halve adaptive bound after two consecutive upper-clamp triggers at the same level; reset if clamp not triggered + if((p47_upperClampActive_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) eq 1) + AND (p47_upperClampActive_iter(iteration-1,ttot,ttot2,ext_regi,emiMktExt) eq 1) + AND (p47_slopeUpperClampBound_iter(iteration-1,ttot,ttot2,ext_regi,emiMktExt) eq p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt)), + p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) = p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) / 2; + elseif(p47_upperClampActive_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) eq 0), + p47_slopeUpperClampBound(ttot,ttot2,ext_regi,emiMktExt) = -0.3; + ); ); ); ); +*** if slope numerator is negligibly small both relative to 2005 emissions AND relative to the remaining gap to close, the slope is degenerate (e.g. initialised from near-net-zero scenario where emi_ref ≈ emi_current ≈ target ≈ 0). +*** fall back to squareDev. It only applies to year targets (pm_emiMktRefYear > 0). +*** The relative-to-gap condition prevents squareDev from firing when emissions are merely close to the target in absolute terms but the slope is still useful for Newton: e.g. a 5 MtCO2 change is below 1% of 2005 emissions but is 25% of a 20 MtCO2 remaining gap, so Newton should be used. + if((regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"slope_refIteration") + OR regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"slope_firstIteration")) + AND NOT((iteration.val - iteration2.val) eq 0) + AND (pm_emiMktRefYear(ttot,ttot2,ext_regi,emiMktExt) gt 0) + AND (abs(p47_emiMktCurrent_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) + - p47_emiMktCurrent_iter(iteration2,ttot,ttot2,ext_regi,emiMktExt)) + lt s47_slopeDegenerateThreshold * pm_emiMktRefYear(ttot,ttot2,ext_regi,emiMktExt)) + AND (abs(p47_emiMktCurrent_iter(iteration,ttot,ttot2,ext_regi,emiMktExt) + - p47_emiMktCurrent_iter(iteration2,ttot,ttot2,ext_regi,emiMktExt)) + lt s47_slopeDegenerateThreshold * abs(pm_emiMktTarget(ttot,ttot2,ext_regi,emiMktExt,target_type_47,emi_type_47) + - p47_emiMktCurrent_iter(iteration,ttot,ttot2,ext_regi,emiMktExt))), + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,rescaleType) = NO; + regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_degenerateSlope") = YES; + pm_factorRescaleemiMktCO2Tax(ttot,ttot2,ext_regi,emiMktExt) = power(1+pm_emiMktTarget_dev(ttot,ttot2,ext_regi,emiMktExt), 2); + ); + ); +*** reset slope reference iteration after squareDev_adaptiveClamp, moved outside loop(iteration2) to prevent the loop from re-evaluating its condition with the updated reference and executing a spurious second body run that sets squareDev_firstIteration alongside the intended type. +*** squareDev_degenerateSlope intentionally does NOT reset the reference: resetting after degeneracy creates a 1-step window on the next iteration that is immediately degenerate again, locking the algorithm out of Newton. Instead, the reference is allowed to age naturally so emissions accumulate enough history for a reliable slope. The slopeMaxWindow mechanism resets the reference after s47_slopeMaxWindow iterations if needed. + if(regiEmiMktRescaleType(iteration,ttot,ttot2,ext_regi,emiMktExt,"squareDev_adaptiveClamp"), + p47_slopeReferenceIteration_iter(iteration,ttot2,ext_regi) = ord(iteration); ); *** dampen if rescale oscillates if( (iteration.val > 3) , @@ -427,7 +476,12 @@ loop(ext_regi$regiEmiMktTarget(ext_regi), pm_taxemiMkt(t,regi,emiMkt)$((t.val gt ttot2.val) AND (t.val lt ttot3.val)) = pm_taxemiMkt(ttot2,regi,emiMkt) + ((pm_taxemiMkt(ttot3,regi,emiMkt) - pm_taxemiMkt(ttot2,regi,emiMkt))/(ttot3.val-ttot2.val))*(t.val-ttot2.val); !! price in between current target year and next target year *** else if next target was never executed by the algorithm yet, initialize next target value as weighted average convergence price between current target terminal year (ttot2.val) and next target year (p47_nextConvergencePeriod) else - p47_averagetaxemiMkt(t,regi) = +*** default: simple average across markets (guards against division by zero when total +*** net emissions are negligibly small, zero, or net-negative with cancellation) + p47_averagetaxemiMkt(t,regi) = + (pm_taxemiMkt(t,regi,"ETS") + pm_taxemiMkt(t,regi,"ES") + pm_taxemiMkt(t,regi,"other")) / 3; +*** override with emission-weighted average when total net emissions are large enough + p47_averagetaxemiMkt(t,regi)$(abs(p47_emiTargetMkt(t,regi,"ETS",emi_type_47) + p47_emiTargetMkt(t,regi,"ESR",emi_type_47) + p47_emiTargetMkt(t,regi,"other",emi_type_47)) gt 1e-6) = (pm_taxemiMkt(t,regi,"ETS")*p47_emiTargetMkt(t,regi,"ETS",emi_type_47) + pm_taxemiMkt(t,regi,"ES")*p47_emiTargetMkt(t,regi,"ESR",emi_type_47) + pm_taxemiMkt(t,regi,"other")*p47_emiTargetMkt(t,regi,"other",emi_type_47)) / (p47_emiTargetMkt(t,regi,"ETS",emi_type_47) + p47_emiTargetMkt(t,regi,"ESR",emi_type_47) + p47_emiTargetMkt(t,regi,"other",emi_type_47)); diff --git a/modules/47_regipol/regiCarbonPrice/sets.gms b/modules/47_regipol/regiCarbonPrice/sets.gms index 631ce0d7f..add29a9e1 100644 --- a/modules/47_regipol/regiCarbonPrice/sets.gms +++ b/modules/47_regipol/regiCarbonPrice/sets.gms @@ -21,10 +21,10 @@ $ifThen.emiMkt not "%cm_emiMktTarget%" == "off" regiEmiMktTarget(ext_regi) "regions with emiMkt targets" / / regiANDperiodEmiMktTarget_47(ttot,ext_regi) "regions and periods with emiMkt targets" / / regiEmiMktTarget2regi_47(ext_regi,all_regi) "regions controlled by emiMkt market set to ext_regi" / / - rescaleType "emi mkt carbon price scaling factor calculation methods" / - "squareDev_firstIteration", "squareDev_perfectMatch", "squareDev_smallChange", "squareDev_noChange", - "slope_refIteration", "slope_firstIteration", "slope_repeatPrev", "slope_repeatPrev_positiveSlope", - "squareDev_noSlope", "squareDev_noNonPositiveSlope"/ + rescaleType "emi mkt carbon price scaling factor calculation methods" / + "squareDev_firstIteration", "squareDev_perfectMatch", "squareDev_smallChange", "squareDev_noChange", + "slope_refIteration", "slope_firstIteration", "slope_repeatPrev_positiveSlope", + "squareDev_noNonPositiveSlope", "squareDev_degenerateSlope", "squareDev_outsideWindow", "squareDev_adaptiveClamp"/ regiEmiMktRescaleType(iteration,ttot,ttot,ext_regi,emiMktExt,rescaleType) "saving scaling type used in iteration" / / convergenceType "emiMkt target non convergence reason" / "lowerThanTolerance", "smallPrice" / regiEmiMktconvergenceType(iteration,ttot,ttot,ext_regi,emiMktExt,convergenceType) "saving convergence type in iteration" / /