From e96ed8f233f819d78601061dc1661f68bbe65e6f Mon Sep 17 00:00:00 2001 From: s1924442 <b.arendarczyk@sms.ed.ac.uk> Date: Thu, 29 Apr 2021 20:30:07 +0100 Subject: [PATCH] Refactored GAMS code. --- GAMS/IntExtOpt.gms | 96 +++++++++---------- src/ac/ed/lurg/ModelConfig.java | 2 +- src/ac/ed/lurg/ModelMain.java | 2 +- .../country/gams/GamsLocationOptimiser.java | 6 +- src/ac/ed/lurg/landuse/WoodYieldItem.java | 15 +++ 5 files changed, 64 insertions(+), 57 deletions(-) diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index caecc0dd..883e6142 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -58,6 +58,7 @@ PARAMETER previousLandCoverArea(land_cover, location) land cover area in Mha; PARAMETER minimumLandCoverArea(land_cover, location) minimum land cover area to constrain conversion in Mha; + PARAMETER unrestrictedArea(land_cover, location) PARAMETER carbonFluxRate(land_cover_before, land_cover_after, location) carbon flux - MtC-eq per Mha; PARAMETER woodYield(land_cover_before, land_cover_after, location) wood yield - MtC-eq per Mha; SCALAR carbonPrice price of carbon - $1000 per tonne; @@ -115,6 +116,7 @@ $gdxin SCALAR forestExpansionMaxRate / 1 /; SCALAR restrictedDeforestationCost / 10000000 /; + unrestrictedArea(land_cover, location) = previousLandCoverArea(land_cover, location) - minimumLandCoverArea(land_cover, location); VARIABLES cropArea(crop, location) total area for crops - Mha @@ -130,26 +132,27 @@ $gdxin net_supply(crop) supply after exports and feed landCoverArea(land_cover, location) land cover area in Mha landCoverChange(land_cover_before, land_cover_after, location) land cover change - deforestedArea(forest, location) area of forest cut down - reservedAreaDeforested(forest, location) area of reserved (minimumLandCoverArea) forest cut down + restrictedLandCoverArea(land_cover, location) + restrictedLandCoverChange(land_cover, land_cover_after, location) prematureDeforestationCost + totalLandCoverArea(land_cover, location) + totalLandCoverChange(land_cover, land_cover_after, location) woodHarvest(location) total wood harvested carbonFlux(location) total carbon flux timberHarvest(location) timberForwardContract(location) -* potentialWoodYield(location) potential wood yield for timber forests planted totalConversionCost(location) totalFeedDM -* A "artificial variable https://www.gams.com/blog/2017/07/misbehaving-model-infeasible/" + A "artificial variable for debugging https://www.gams.com/blog/2017/07/misbehaving-model-infeasible/" total_cost total cost of domestic supply including net imports; - POSITIVE VARIABLE cropArea, fertI, irrigI, otherIntensity, ruminantFeed, monogastricFeed, importAmount, exportAmount, totalFeedDM, - deforestedArea, reservedAreaDeforested, prematureDeforestationCost, - landCoverArea, landCoverChange, woodHarvest, timberHarvest, timberForwardContract, totalConversionCost; + prematureDeforestationCost, + landCoverArea, landCoverChange, woodHarvest, timberHarvest, timberForwardContract, totalConversionCost, + restrictedLandCoverArea, restrictedLandCoverChange, totalLandCoverArea, totalLandCoverChange; -* POSITIVE VARIABLE A; + POSITIVE VARIABLE A; EQUATIONS UNIT_COST_EQ(crop, location) cost per area - $1000 per ha or $billion per Mha @@ -165,7 +168,7 @@ $gdxin MAX_IRRIG_INTENSITY_CONSTRAINT(crop, location) constraint on maximum irrigation intensity MAX_OTHER_INTENSITY_CONSTRAINT(crop, location) SETASIDE_AREA_CALC(location) - TOTAL_LAND_CHANGE_CONSTRAINT(location) constraint on suitable land use +* TOTAL_LAND_CHANGE_CONSTRAINT(location) constraint on suitable land use MAX_NET_IMPORT_CONSTRAINT(import_crop) constraint on max net imports MIN_NET_IMPORT_CONSTRAINT(import_crop) constraint on min net imports PASTURE_IMPORT_CONSTRAINT constraint to not import pasture @@ -174,21 +177,19 @@ $gdxin NET_SUPPLY_EQ(crop) calc net supply for crops CROPLAND_LAND_COVER_CALC(location) cropland area equals sum of all crop areas PASTURE_LAND_COVER_CALC(location) pasture area (land cover) equals pasture area (land use) -* MINIMUM_LAND_COVER_CONSTRAINT(land_cover, location) constraint on land cover conversion LAND_COVER_CHANGE_CALC(land_cover, location) LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) conservation of land area -* LAND_COVER_SELF_CHANGE_CONSTRAINT(land_cover, location) calculate no change area (e.g. cropland to cropland) + RESTRICTED_LAND_COVER_CHANGE_CALC(land_cover, location) + RESTRICTED_LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) FOREST_EXPANSION_CONSTRAINT - DEFORESTED_AREA_CALC(forest, location) - RESERVED_AREA_DEFORESTED_CALC(forest, location) PREMATURE_DEFORESTATION_COST_CALC -* UNMANAGED_FOREST_CONSTRAINT(location) unmanagedForest cannot be created + TOTAL_LAND_COVER_CALC(land_cover, location) + TOTAL_LAND_COVER_CHANGE_CALC(land_cover, land_cover_after, location) +* TOTAL_LAND_COVER_CONSTRAINT(location) WOOD_HARVEST_CALC(location) calc wood harvested CARBON_FLUX_CALC(location) calc carbon flux TIMBER_HARVEST_CALC(location) TIMBER_FORWARD_CONTRACT_CALC(location) -* POTENTIAL_WOOD_YIELD_CALC(location) -* TIMBER_FOREST_CHANGE_CONSTRAINT(location) forcing mature timber forest to be cut down CONVERSION_COST(location) COST_EQ total cost objective function; @@ -227,10 +228,12 @@ $gdxin SETASIDE_AREA_CALC(location) .. cropArea('setaside', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) * setAsideRate; -* TOTAL_LAND_CHANGE_CONSTRAINT(location) .. suitableLandArea(location) =G= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) / (1.0 - unhandledCropRate) + cropArea('pasture', location); + TOTAL_LAND_COVER_CALC(land_cover, location) .. totalLandCoverArea(land_cover, location) =E= landCoverArea(land_cover, location) + restrictedLandCoverArea(land_cover, location); + +* TOTAL_LAND_COVER_CONSTRAINT(location) .. sum(land_cover, totalLandCoverArea(land_cover, location)) =E= sum(land_cover, previousLandCoverArea(land_cover, location)); -* Needs inequality due to floating point errors - TOTAL_LAND_CHANGE_CONSTRAINT(location) .. suitableLandArea(location) =G= sum(land_cover, landCoverArea(land_cover, location)); +* Needs inequality due to floating point errors but in theory should be equal +* TOTAL_LAND_CHANGE_CONSTRAINT(location) .. suitableLandArea(location) =G= sum(land_cover, totalLandCoverArea(land_cover, location)); MAX_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =L= maxNetImport(import_crop); MIN_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =G= minNetImport(import_crop); @@ -239,59 +242,45 @@ $gdxin IRRIGATION_CONSTRAINT(location) .. irrigConstraint(location) * suitableLandArea(location) * (1.0 - unhandledCropRate) =G= sum(crop, irrigMaxRate(crop, location) * irrigI(crop, location) * cropArea(crop, location)); - CROPLAND_LAND_COVER_CALC(location) .. landCoverArea('cropland', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) / (1.0 - unhandledCropRate); + CROPLAND_LAND_COVER_CALC(location) .. totalLandCoverArea('cropland', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) / (1.0 - unhandledCropRate); - PASTURE_LAND_COVER_CALC(location) .. landCoverArea('pasture', location) =E= cropArea('pasture', location); + PASTURE_LAND_COVER_CALC(location) .. totalLandCoverArea('pasture', location) =E= cropArea('pasture', location); - LAND_COVER_CHANGE_CALC(land_cover, location) .. landCoverArea(land_cover, location) =E= previousLandCoverArea(land_cover, location) + + LAND_COVER_CHANGE_CALC(land_cover, location) .. landCoverArea(land_cover, location) =E= unrestrictedArea(land_cover, location) + sum(land_cover_before, landCoverChange(land_cover_before, land_cover, location)) - sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)); -* landCoverChange(A, A, location) defined as unchanged land cover - LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) .. sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)) =E= previousLandCoverArea(land_cover, location); - -* is this needed or can it be handled by keeping track of wood yields? -* TIMBER_FOREST_CHANGE_CONSTRAINT(location) .. sum(land_cover_after, landCoverChange('timberForest', land_cover_after, location)) =G= matureTimberForestArea(location); - - FOREST_EXPANSION_CONSTRAINT .. sum((non_forest, forest, location), landCoverChange(non_forest, forest, location)) =L= sum((non_forest, location), previousLandCoverArea(non_forest, location)) * forestExpansionMaxRate; + RESTRICTED_LAND_COVER_CHANGE_CALC(land_cover, location) .. restrictedLandCoverArea(land_cover, location) =E= minimumLandCoverArea(land_cover, location) + + sum(land_cover_before, restrictedLandCoverChange(land_cover_before, land_cover, location)) - + sum(land_cover_after, restrictedLandCoverChange(land_cover, land_cover_after, location)); -* MINIMUM_LAND_COVER_CONSTRAINT(land_cover, location) .. landCoverChange(land_cover, land_cover, location) =G= minimumLandCoverArea(land_cover, location); + TOTAL_LAND_COVER_CHANGE_CALC(land_cover, land_cover_after, location) .. totalLandCoverChange(land_cover, land_cover_after, location) =E= landCoverChange(land_cover, land_cover_after, location) + + restrictedLandCoverChange(land_cover, land_cover_after, location); -* PREMATURE_DEFORESTATION_COST_CALC(location) .. prematureDeforestationCost(location) =E= sum(forest, (sum((land_cover_after)$[not sameAs(forest, land_cover_after)], landCoverChange(forest, land_cover_after, location)) - -* previousLandCoverArea(forest, location) - minimumLandCoverArea(forest, location))); - - DEFORESTED_AREA_CALC(forest, location) .. deforestedArea(forest, location) =E= sum((land_cover_after)$[not sameAs(forest, land_cover_after)], landCoverChange(forest, land_cover_after, location)); - -* Equivalent to MAX(0, deforestedArea(forest, location) - minimumLandCoverArea(forest, location)) https://www.gams.com/latest/docs/UG_NLP_GoodFormulations.html#UG_NLP_GoodFormulations_ReformulatingDNLPModels - RESERVED_AREA_DEFORESTED_CALC(forest, location) .. reservedAreaDeforested(forest, location) =E= [(deforestedArea(forest, location) - [previousLandCoverArea(forest, location) - minimumLandCoverArea(forest, location)] + delta) + - sqrt(sqr(deforestedArea(forest, location) - [previousLandCoverArea(forest, location) - minimumLandCoverArea(forest, location)] + delta)) + - sqr(delta)] / 2; +* landCoverChange(A, A, location) defined as unchanged land cover + LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) .. sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)) =E= unrestrictedArea(land_cover, location); - PREMATURE_DEFORESTATION_COST_CALC .. prematureDeforestationCost =E= sum((forest, location), reservedAreaDeforested(forest, location)) * restrictedDeforestationCost; + RESTRICTED_LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) .. sum(land_cover_after, restrictedLandCoverChange(land_cover, land_cover_after, location)) =E= minimumLandCoverArea(land_cover, location); -* UNMANAGED_FOREST_CONSTRAINT(location) .. sum((land_cover)$[not sameAs(land_cover, 'unmanagedForest')], landCoverChange(land_cover, 'unmanagedForest', location)) =E= 0; + FOREST_EXPANSION_CONSTRAINT .. sum((non_forest, forest, location), landCoverChange(non_forest, forest, location)) =L= sum((non_forest, location), previousLandCoverArea(non_forest, location)) * forestExpansionMaxRate; -* POTENTIAL_WOOD_YIELD_CALC(location) .. potentialWoodYield(location) =E= sum(land_cover_before, landCoverChange(land_cover_before, 'timberForest', location) * woodYield(land_cover_before, 'timberForest', location)) / -* sum(land_cover_before, landCoverChange(land_cover_before, 'timberForest', location)); + PREMATURE_DEFORESTATION_COST_CALC .. prematureDeforestationCost =E= sum((forest, land_cover, location)$[not sameAs(forest, land_cover)], restrictedLandCoverChange(forest, land_cover, location)) * restrictedDeforestationCost; * Timber from land cover change WOOD_HARVEST_CALC(location) .. woodHarvest(location) =E= sum((non_timber_forest_before, non_timber_forest_after), landCoverChange(non_timber_forest_before, non_timber_forest_after, location) * woodYield(non_timber_forest_before, non_timber_forest_after, location)); * Timber from timberForest rotation - TIMBER_HARVEST_CALC(location) .. timberHarvest(location) =E= (sum(land_cover_after, landCoverChange('timberForest', land_cover_after, location)) - minimumLandCoverArea('timberForest', location)) * timberForestYield(location); + TIMBER_HARVEST_CALC(location) .. timberHarvest(location) =E= (sum(land_cover_after, landCoverChange('timberForest', land_cover_after, location))) * timberForestYield(location); * Future timber from newly planted timberForest TIMBER_FORWARD_CONTRACT_CALC(location) .. timberForwardContract(location) =E= sum(non_timber_forest_before, landCoverChange(non_timber_forest_before, 'timberForest', location) * woodYield(non_timber_forest_before, 'timberForest', location)) + - (landCoverChange('timberForest', 'timberForest', location) - minimumLandCoverArea('timberForest', location)) * woodYield('timberForest', 'timberForest', location); + (landCoverChange('timberForest', 'timberForest', location)) * woodYield('timberForest', 'timberForest', location); - CARBON_FLUX_CALC(location) .. carbonFlux(location) =E= sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * carbonFluxRate(land_cover_before, land_cover_after, location)); + CARBON_FLUX_CALC(location) .. carbonFlux(location) =E= sum((land_cover_before, land_cover_after), totalLandCoverChange(land_cover_before, land_cover_after, location) * carbonFluxRate(land_cover_before, land_cover_after, location)); * Subtracting cost of not fully grown timberForest reserved under minimumLandCoverArea - CONVERSION_COST(location) .. totalConversionCost(location) =E= sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * conversionCost(land_cover_before, land_cover_after)); - -* CROPLAND_INCREASE_CONSTRAINT(location) .. cropIncrease(location)/(1.0 - unhandledCropRate) =L= maxLandChangeRate * ( suitableLandArea(location) -sum(crop_less_pasture, previousCropArea(crop_less_pasture, location))/*(1.0 - unhandledCropRate) - previousCropArea('pasture', location) ); -* PASTURE_DECREASE_CONSTRAINT(location) .. pastureDecrease(location) =L= maxLandChangeRate * previousCropArea('pasture', location); + CONVERSION_COST(location) .. totalConversionCost(location) =E= sum((land_cover_before, land_cover_after), totalLandCoverChange(land_cover_before, land_cover_after, location) * conversionCost(land_cover_before, land_cover_after)); COST_EQ .. total_cost =E= ( @@ -301,7 +290,7 @@ $gdxin SUM(import_crop, importAmount(import_crop) * importPrices(import_crop) - exportAmount(import_crop) * exportPrices(import_crop)) + - prematureDeforestationCost + SUM(location, totalConversionCost(location)) + prematureDeforestationCost + SUM(location, totalConversionCost(location)) + A * 1000000 ); MODEL LAND_USE /ALL/ ; @@ -309,7 +298,8 @@ $gdxin irrigI.L(crop, location) = previousIrrigIntensity(crop, location); otherIntensity.L(crop, location) = previousOtherIntensity(crop, location); cropArea.L(crop, location) = previousCropArea(crop, location); - landCoverArea.L(land_cover, location) = previousLandCoverArea(land_cover, location); + landCoverArea.L(land_cover, location) = previousLandCoverArea(land_cover, location) - minimumLandCoverArea(land_cover, location); + restrictedLandCoverArea.L(land_cover, location) = minimumLandCoverArea(land_cover, location); ruminantFeed.L(feed_crop) = previousRuminantFeed(feed_crop); monogastricFeed.L(feed_crop) = previousMonogastricFeed(feed_crop); importAmount.L(all_types) = previousImportAmount(all_types); @@ -329,6 +319,7 @@ $gdxin parameter netImportCost(all_types); parameter feedCostRate(feed_crop); parameter productionShock(all_types); + parameter reservedAreaDeforested(forest, location); scalar netCarbonFlux; scalar totalTimberProduction; @@ -351,6 +342,7 @@ $gdxin netImportCost(import_crop) = importAmount.l(import_crop) * importPrices(import_crop) - exportAmount.l(import_crop) * exportPrices(import_crop); netImportAmount(import_crop) = importAmount.l(import_crop) - exportAmount.l(import_crop); + reservedAreaDeforested(forest, location) = sum((land_cover)$[not sameAs(forest, land_cover)], restrictedLandCoverChange.L(forest, land_cover, location)); netCarbonFlux = SUM(location, carbonFlux.L(location)); totalTimberProduction = SUM(location, woodHarvest.L(location) + timberHarvest.L(location)); diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index 7009cb27..59f788e7 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -249,7 +249,7 @@ public class ModelConfig { // Wood/carbon data public static final String FOREST_DIR = SPATIAL_DATA_DIR + File.separator + "forestry"; //public static final String CARBON_FLUX_FILE = FOREST_DIR + File.separator + "carbon_flux_"; - //public static final String WOOD_YIELD_FILE = FOREST_DIR + File.separator + "wood_yield_"; + public static final String WOOD_YIELD_FILENAME = "wood_yield.out"; // Output public static final String LAND_COVER_OUTPUT_FILE = OUTPUT_DIR + File.separator + "lc.txt"; diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java index ca92fa0f..1aa7294b 100644 --- a/src/ac/ed/lurg/ModelMain.java +++ b/src/ac/ed/lurg/ModelMain.java @@ -678,7 +678,7 @@ public class ModelMain { private WoodYieldRasterSet getWoodYieldData(Timestep timestep) { WoodYieldRasterSet woodYieldData = new WoodYieldRasterSet(desiredProjection); LogWriter.println("Reading wood yield data"); - new WoodYieldReader(woodYieldData).getRasterDataFromFile(timestep.getYearSubDir(ModelConfig.FOREST_DIR) + File.separator + "wood_yield.out"); + new WoodYieldReader(woodYieldData).getRasterDataFromFile(timestep.getYearSubDir(ModelConfig.FOREST_DIR) + File.separator + ModelConfig.WOOD_YIELD_FILENAME); return woodYieldData; } diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index ac22ad3f..078e720b 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -640,15 +640,15 @@ public class GamsLocationOptimiser { // reserved Forest which was deforested - GAMSVariable varReservedDeforested = outDB.getVariable("reservedAreaDeforested"); + GAMSParameter varReservedDeforested = outDB.getParameter("reservedAreaDeforested"); DoubleMap<Integer, LandCoverType, Double> reservedDeforested = new DoubleMap<Integer, LandCoverType, Double>(); - for (GAMSVariableRecord rec : varReservedDeforested) { + for (GAMSParameterRecord rec : varReservedDeforested) { String landCover = rec.getKeys()[0]; String locationName = rec.getKeys()[1]; int locId = Integer.parseInt(locationName); - double deforestedArea = rec.getLevel(); + double deforestedArea = rec.getValue(); if (deforestedArea > 0.00001) { reservedDeforested.put(locId, LandCoverType.getForName(landCover), deforestedArea); diff --git a/src/ac/ed/lurg/landuse/WoodYieldItem.java b/src/ac/ed/lurg/landuse/WoodYieldItem.java index 3da28759..87214369 100644 --- a/src/ac/ed/lurg/landuse/WoodYieldItem.java +++ b/src/ac/ed/lurg/landuse/WoodYieldItem.java @@ -8,6 +8,7 @@ import ac.sac.raster.RasterItem; public class WoodYieldItem implements RasterItem { Map<LandCoverType, Map<LandCoverType, Double>> woodYields = new HashMap<LandCoverType, Map<LandCoverType, Double>>(); + Map<LandCoverType, Map<LandCoverType, Double>> timberYields = new HashMap<LandCoverType, Map<LandCoverType, Double>>(); public void setWoodYield(LandCoverType previousLandCover, LandCoverType newLandCover, double woodYield) { if (woodYields.containsKey(previousLandCover)) { @@ -19,10 +20,24 @@ public class WoodYieldItem implements RasterItem { } } + public void setTimberYield(LandCoverType previousLandCover, LandCoverType newLandCover, double woodYield) { + if (timberYields.containsKey(previousLandCover)) { + timberYields.get(previousLandCover).put(newLandCover, woodYield); + } else { + Map<LandCoverType, Double> cfMap = new HashMap<LandCoverType, Double>(); + cfMap.put(newLandCover, woodYield); + timberYields.put(previousLandCover, cfMap); + } + } + public double getWoodYield(LandCoverType previousLandCover, LandCoverType newLandCover) { return woodYields.get(previousLandCover).get(newLandCover); } + public double getTimberYield(LandCoverType previousLandCover, LandCoverType newLandCover) { + return timberYields.get(previousLandCover).get(newLandCover); + } + public boolean checkForKeys(LandCoverType previousLandCover, LandCoverType newLandCover) { if (woodYields.containsKey(previousLandCover)) { if (woodYields.get(previousLandCover).containsKey(newLandCover)) { -- GitLab