From 892937ce58545f561ddc6a750bb84cf01778a5a8 Mon Sep 17 00:00:00 2001 From: florianh Date: Sun, 4 Oct 2026 16:37:30 +0200 Subject: [PATCH 1/3] Remove redundant m_boundfix calls, fix to up instead of l The calls in 10_land (runs before modules 31-35 set the vm_land bounds) and 29_cropland tree cover (variable fixed one line above) never change anything. At the remaining l sites, .l equals .up when the macro fires, so up gives identical results without depending on statement order. --- CHANGELOG.md | 1 + modules/10_land/landmatrix_dec18/presolve.gms | 2 -- modules/29_cropland/detail_apr24/presolve.gms | 3 +-- modules/35_natveg/pot_forest_may24/presolve.gms | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 903a980eff..2d900a583b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### changed +- **10_land, 29_cropland, 35_natveg** removed two redundant `m_boundfix` calls (10_land, 29_cropland tree cover) and fix to `up` instead of `l` (no change in results) - **39_landconversion/calib/input.gms**: default.cfg sets 5904 (4800 * 1.23), so changed the GAMS default to match. - **default.cfg** module 39: Adjusted description for pasture calibration in module 39, adding that `cfg$gms$s39_ignore_calib_past <- 1 ` switches the pasture factors off in GAMS without any recalibration. - **default.cfg** module 39: exposed `s39_reward_past_reduction` and `s39_ignore_calib_past` as configurable (previously hardcoded); rescaled `s39_reward_past_reduction` default from 7380 to 5904 to match cropland's reward/cost ratio; updated stale realization comment describing pasture cost as static diff --git a/modules/10_land/landmatrix_dec18/presolve.gms b/modules/10_land/landmatrix_dec18/presolve.gms index 1a47df4129..1b464a8594 100644 --- a/modules/10_land/landmatrix_dec18/presolve.gms +++ b/modules/10_land/landmatrix_dec18/presolve.gms @@ -21,5 +21,3 @@ vm_lu_transitions.fx(j,land_from,"primforest") = 0; vm_lu_transitions.up(j,"primforest","primforest") = Inf; *' @stop - -m_boundfix(vm_land,(j,land),up,1e-6); diff --git a/modules/29_cropland/detail_apr24/presolve.gms b/modules/29_cropland/detail_apr24/presolve.gms index 1d9b8e0fbd..bad031e0f4 100644 --- a/modules/29_cropland/detail_apr24/presolve.gms +++ b/modules/29_cropland/detail_apr24/presolve.gms @@ -79,7 +79,6 @@ if (s29_treecover_keep = 1, v29_treecover.lo(j,ac_est) = 0; v29_treecover.up(j,ac_est) = Inf; v29_treecover.fx(j,ac_sub) = pc29_treecover(j,ac_sub); -m_boundfix(v29_treecover,(j,ac_sub),l,1e-6); * set treecover penalty if (m_year(t) <= s29_treecover_scenario_start, @@ -119,7 +118,7 @@ else * Bounds for fallow land vm_fallow.lo(j) = 0; vm_fallow.up(j) = p29_avl_cropland(t,j); -m_boundfix(vm_fallow,(j),l,1e-6); +m_boundfix(vm_fallow,(j),up,1e-6); * Update biodiversity value vm_bv.l(j,"crop_fallow",potnatveg) = diff --git a/modules/35_natveg/pot_forest_may24/presolve.gms b/modules/35_natveg/pot_forest_may24/presolve.gms index 64297dd481..75bc2a6825 100644 --- a/modules/35_natveg/pot_forest_may24/presolve.gms +++ b/modules/35_natveg/pot_forest_may24/presolve.gms @@ -166,7 +166,7 @@ v35_secdforest.lo(j,ac_sub) = max((1-s35_natveg_harvest_shr) * pc35_secdforest(j ); * upper bound v35_secdforest.up(j,ac_sub) = pc35_secdforest(j,ac_sub); -m_boundfix(v35_secdforest,(j,ac_sub),l,1e-6); +m_boundfix(v35_secdforest,(j,ac_sub),up,1e-6); * set restoration target p35_land_restoration(j,"secdforest") = pm_land_conservation(t,j,"secdforest","restore"); @@ -197,7 +197,7 @@ vm_land_other.up(j,"othernat",ac_sub) = pc35_land_other(j,"othernat",ac_sub); * No shr limit on other (only secdforest) vm_land_other.up(j,"youngsecdf",ac_sub) = pc35_land_other(j,"youngsecdf",ac_sub); vm_land_other.fx(j,"youngsecdf",ac_est) = 0; -m_boundfix(vm_land_other,(j,othertype35,ac_sub),l,1e-6); +m_boundfix(vm_land_other,(j,othertype35,ac_sub),up,1e-6); pm_max_forest_est(t,j)$(pm_max_forest_est(t,j) < sum(ac, pc35_land_other(j,"youngsecdf",ac))) = sum(ac, pc35_land_other(j,"youngsecdf",ac)); From 4dc8e890a09ccb00b850ed2005cfe9916b09c5da Mon Sep 17 00:00:00 2001 From: florianh Date: Mon, 5 Oct 2026 11:29:42 +0200 Subject: [PATCH 2/3] CHANGELOG: move boundfix entry to fixed --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d900a583b..208908f08b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### changed -- **10_land, 29_cropland, 35_natveg** removed two redundant `m_boundfix` calls (10_land, 29_cropland tree cover) and fix to `up` instead of `l` (no change in results) - **39_landconversion/calib/input.gms**: default.cfg sets 5904 (4800 * 1.23), so changed the GAMS default to match. - **default.cfg** module 39: Adjusted description for pasture calibration in module 39, adding that `cfg$gms$s39_ignore_calib_past <- 1 ` switches the pasture factors off in GAMS without any recalibration. - **default.cfg** module 39: exposed `s39_reward_past_reduction` and `s39_ignore_calib_past` as configurable (previously hardcoded); rescaled `s39_reward_past_reduction` default from 7380 to 5904 to match cropland's reward/cost ratio; updated stale realization comment describing pasture cost as static @@ -42,6 +41,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **52_carbon/35_natveg/32_forestry** Retired workarounds that are no longer needed once the wood calibration is separated from the carbon growth curve: the k-bisection growth-curve calibration, the "natural-origin" secondary-forest carbon blend and harvest floor, and the young-secondary-forest wood-harvest loophole. ### fixed +- **10_land, 29_cropland, 35_natveg** `m_boundfix` fixes to `up` instead of `l`, removed two redundant calls (10_land, 29_cropland tree cover) (no change in results) - **scripts/calibration/landconversion_cost.R** `restart=TRUE` now reports cropland/pasture calibration-file state independently instead of assuming they match; best-iteration selection now indexes calibration factors by iteration name rather than array position, avoiding potential misalignment From fde38119b2b813cab0ddaf806491300fb0b8a832 Mon Sep 17 00:00:00 2001 From: florianh Date: Thu, 8 Oct 2026 10:26:13 +0200 Subject: [PATCH 3/3] Simplify m_boundfix to (x,arg), keep call in 10_land The macro always fixes to up with sensitivity 1e-6, the values used at all call sites. Keep the 10_land call, since not every realization of 29-35 resets the vm_land bounds (review by tscheypidi). --- CHANGELOG.md | 2 +- core/macros.gms | 9 ++++----- modules/10_land/landmatrix_dec18/presolve.gms | 2 ++ modules/29_cropland/detail_apr24/presolve.gms | 2 +- modules/32_forestry/dynamic_may24/presolve.gms | 2 +- modules/35_natveg/pot_forest_may24/presolve.gms | 6 +++--- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3fdaa2365..6a7b9b37b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **52_carbon/35_natveg/32_forestry** Retired workarounds that are no longer needed once the wood calibration is separated from the carbon growth curve: the k-bisection growth-curve calibration, the "natural-origin" secondary-forest carbon blend and harvest floor, and the young-secondary-forest wood-harvest loophole. ### fixed -- **10_land, 29_cropland, 35_natveg** `m_boundfix` fixes to `up` instead of `l`, removed two redundant calls (10_land, 29_cropland tree cover) (no change in results) +- **core, 29_cropland, 35_natveg** `m_boundfix` always fixes to `up` with sensitivity 1e-6 (arguments `sufx` and `sens` removed, previously `l` at three sites), removed redundant call for 29_cropland tree cover (no change in results) - **scripts/npi_ndc** NPI/NDC avoided deforestation and other land conversion bounds now follow the policy targets. Previously, targets were ignored and the bound was frozen at the last observed stock. The minimum stock is now the last observed stock minus the cumulative allowed loss, and the NDC trajectory continues from the NPI trajectory after 2025 - **scripts/calibration/landconversion_cost.R** `restart=TRUE` now reports cropland/pasture calibration-file state independently instead of assuming they match; best-iteration selection now indexes calibration factors by iteration name rather than array position, avoiding potential misalignment diff --git a/core/macros.gms b/core/macros.gms index 881ba0fd61..a85311b224 100644 --- a/core/macros.gms +++ b/core/macros.gms @@ -7,11 +7,10 @@ *****************************Technical macros*********************************** -* Macro for fixing a variable in the case that lower and upper bound are too -* close to each other (closer than argument "sens"). -* "arg" specifies the set for fixing. "sufx" specifies to what value the variable should be fixed ("l","lo","up"). -* EXAMPLE: m_boundfix(vm_land,(j,"ifft"),up,1e-6); -$macro m_boundfix(x,arg,sufx,sens) x.fx arg$(x.up arg-x.lo arg upper from rounding). "arg" specifies the set for fixing. +* EXAMPLE: m_boundfix(vm_land,(j,"ifft")); +$macro m_boundfix(x,arg) x.fx arg$(x.up arg-x.lo arg<1e-6) = x.up arg; $macro m_weightedmean(x,w,s) (sum(s,x*w)/sum(s,w))$(sum(s,w)>0) + 0$(sum(s,w)<=0); diff --git a/modules/10_land/landmatrix_dec18/presolve.gms b/modules/10_land/landmatrix_dec18/presolve.gms index 1b464a8594..95d2fd9d7a 100644 --- a/modules/10_land/landmatrix_dec18/presolve.gms +++ b/modules/10_land/landmatrix_dec18/presolve.gms @@ -21,3 +21,5 @@ vm_lu_transitions.fx(j,land_from,"primforest") = 0; vm_lu_transitions.up(j,"primforest","primforest") = Inf; *' @stop + +m_boundfix(vm_land,(j,land)); diff --git a/modules/29_cropland/detail_apr24/presolve.gms b/modules/29_cropland/detail_apr24/presolve.gms index bad031e0f4..8fd369f99b 100644 --- a/modules/29_cropland/detail_apr24/presolve.gms +++ b/modules/29_cropland/detail_apr24/presolve.gms @@ -118,7 +118,7 @@ else * Bounds for fallow land vm_fallow.lo(j) = 0; vm_fallow.up(j) = p29_avl_cropland(t,j); -m_boundfix(vm_fallow,(j),up,1e-6); +m_boundfix(vm_fallow,(j)); * Update biodiversity value vm_bv.l(j,"crop_fallow",potnatveg) = diff --git a/modules/32_forestry/dynamic_may24/presolve.gms b/modules/32_forestry/dynamic_may24/presolve.gms index 7af5199509..7bf38ee2b2 100644 --- a/modules/32_forestry/dynamic_may24/presolve.gms +++ b/modules/32_forestry/dynamic_may24/presolve.gms @@ -180,7 +180,7 @@ else *' No afforestation is allowed if carbon density <= 20 tc/ha v32_land.fx(j,"aff",ac_est)$(fm_carbon_density(t,j,"forestry","vegc") <= 20) = 0; -m_boundfix(v32_land,(j,type32,ac_sub),up,1e-6); +m_boundfix(v32_land,(j,type32,ac_sub)); ** Calculate expected growing stock at rotation age i32_growing_stock_at_harvest(t,j) = sum(ac$(ac.off = pm_rotation_cellular_estb(t,j)), im_growing_stock(t,j,ac,"forestry")); diff --git a/modules/35_natveg/pot_forest_may24/presolve.gms b/modules/35_natveg/pot_forest_may24/presolve.gms index 75bc2a6825..952223a74a 100644 --- a/modules/35_natveg/pot_forest_may24/presolve.gms +++ b/modules/35_natveg/pot_forest_may24/presolve.gms @@ -166,7 +166,7 @@ v35_secdforest.lo(j,ac_sub) = max((1-s35_natveg_harvest_shr) * pc35_secdforest(j ); * upper bound v35_secdforest.up(j,ac_sub) = pc35_secdforest(j,ac_sub); -m_boundfix(v35_secdforest,(j,ac_sub),up,1e-6); +m_boundfix(v35_secdforest,(j,ac_sub)); * set restoration target p35_land_restoration(j,"secdforest") = pm_land_conservation(t,j,"secdforest","restore"); @@ -197,7 +197,7 @@ vm_land_other.up(j,"othernat",ac_sub) = pc35_land_other(j,"othernat",ac_sub); * No shr limit on other (only secdforest) vm_land_other.up(j,"youngsecdf",ac_sub) = pc35_land_other(j,"youngsecdf",ac_sub); vm_land_other.fx(j,"youngsecdf",ac_est) = 0; -m_boundfix(vm_land_other,(j,othertype35,ac_sub),up,1e-6); +m_boundfix(vm_land_other,(j,othertype35,ac_sub)); pm_max_forest_est(t,j)$(pm_max_forest_est(t,j) < sum(ac, pc35_land_other(j,"youngsecdf",ac))) = sum(ac, pc35_land_other(j,"youngsecdf",ac)); @@ -217,7 +217,7 @@ pm_land_conservation(t,j,"other","restore") = p35_land_restoration(j,"other"); vm_land.lo(j,"other") = pm_land_conservation(t,j,"other","protect") + p35_land_restoration(j,"other"); * boundfix for land_natveg -m_boundfix(vm_land,(j,land_natveg),up,1e-6); +m_boundfix(vm_land,(j,land_natveg)); * ---------------------------- * Calculate carbon density