diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index d3cf16ca35792b711aed03b9ba6f87a7d8d1c09a..db5f3d5ff0cc7231f464108beee7e938bc358959 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -52,6 +52,7 @@ PARAMETER subsidyRate(all_types) rates of subsidy compared to costs; PARAMETER previousLandCoverArea(land_cover, location) land cover area in Mha; + PARAMETER maxCroplandArea(location); PARAMETER carbonCreditRate(land_cover_before, land_cover_after, location) potential carbon credits from LULUCF - tC-eq per ha; PARAMETER conversionCost(land_cover_before, land_cover_after, location) cost of converting from one land cover to another - $1000 per ha; PARAMETER woodYieldMax(location); @@ -84,7 +85,7 @@ $load previousCropArea, previousFertIntensity, previousIrrigIntensity, previousO $load yieldNone, yieldFertOnly, yieldIrrigOnly, yieldBoth, yieldShock $load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxNetImport, minNetImport, unhandledCropRate, setAsideRate, maxGrossLccRate, subsidyRate $load meatEfficency, otherICost, irrigCost, irrigMaxRate, irrigConstraint, fertiliserUnitCost, domesticPriceMarkup, minDemandFraction, seedAndWasteRate -$load previousLandCoverArea, carbonCreditRate, conversionCost, woodYieldMax, woodYieldParam, maxRotationIntensity, forestBaseCost, tradeAdjustmentCostRate +$load previousLandCoverArea, maxCroplandArea, carbonCreditRate, conversionCost, woodYieldMax, woodYieldParam, maxRotationIntensity, forestBaseCost, tradeAdjustmentCostRate $load forestManagementCost, vegClearingCostRate, carbonForestMaxProportion, maxFertChange, maxIrrigChange, previousRotationIntensity $gdxin @@ -286,7 +287,10 @@ $gdxin LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) .. sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)) =E= previousLandCoverArea(land_cover, location); CONVERSION_COST_CALC(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, location)); + landCoverChange(land_cover_before, land_cover_after, location) * conversionCost(land_cover_before, land_cover_after, location)); + +* Cropland area restriction due to slope + landCoverArea.UP('cropland', location) = maxCroplandArea(location); * MIN_LAND_COVER_AREA_CONSTRAINT(land_cover, location) .. landCoverArea(land_cover, location) =G= minLandCoverArea(land_cover, location); diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index 86775fcc41f1bcf603f3e93a767b7e4c04e683e6..05f69af971860b8fc675044489b141d1b00a1d05 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -129,6 +129,7 @@ public class GamsLocationOptimiser { GAMSParameter seedAndWasteRateP = inDB.addParameter("seedAndWasteRate", 1); GAMSParameter prevLandCoverP = inDB.addParameter("previousLandCoverArea", 2); + GAMSParameter maxCroplandAreaP = inDB.addParameter("maxCroplandArea", 1); GAMSParameter prevRotaIntP = inDB.addParameter("previousRotationIntensity", 1); double totalAgriLand = 0; @@ -197,7 +198,7 @@ public class GamsLocationOptimiser { setGamsParamValueTruncate(prevLandCoverP.addRecord(v), area, 6); } - double foo = landUseItem.getForestRotationIntensity(); + setGamsParamValue(maxCroplandAreaP.addRecord(locString), landUseItem.getMaxCroplandArea(), 6); setGamsParamValue(prevRotaIntP.addRecord(locString), landUseItem.getForestRotationIntensity(), 6); } diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java index f671b3cddb380c01115aa508099d27890d79491c..019d50d9803c061f69949fa3f214b908ef4d85ab 100644 --- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java @@ -161,6 +161,7 @@ public class GamsRasterOptimiser { keys.add(key); } } + for (LandCoverChangeItem item : changes) { LandCoverType fromLc = item.getFromLandCover(); LandCoverType toLc = item.getToLandCover(); @@ -170,12 +171,14 @@ public class GamsRasterOptimiser { double totalArea = 0; for (RasterKey key : keys) { - totalArea += luRaster.get(key).getLandCoverArea(fromLc, LandProtectionType.CONVERTIBLE); + double area = luRaster.get(key).getLandCoverArea(fromLc, LandProtectionType.CONVERTIBLE); + totalArea += area; } for (RasterKey key : keys) { double area = luRaster.get(key).getLandCoverArea(fromLc, LandProtectionType.CONVERTIBLE); double fraction = area / totalArea; + // Splitting up the area changes in clusters proportionally by areas in raster cells double changedArea = fraction * totalChangeArea; if (changedArea < 2 * Double.MIN_VALUE) { @@ -354,6 +357,9 @@ public class GamsRasterOptimiser { aggLandUse.setLandCoverArea(lcType, LandProtectionType.PROTECTED, protAreaSoFar + protAreaThisTime); } + // Maximum cropland area + aggLandUse.setMaxCroplandArea(aggLandUse.getMaxCroplandArea() + landUseItem.getMaxCroplandArea()); + aggLandUse.setForestRotationIntensity(landUseItem.getForestRotationIntensity()); } diff --git a/src/ac/ed/lurg/landuse/LandCoverItem.java b/src/ac/ed/lurg/landuse/LandCoverItem.java index 861d8d93b53e64a0c644a717c3cc7dfffe230363..6638bd7f68ef603b93727d976be8f3be3e432a7e 100644 --- a/src/ac/ed/lurg/landuse/LandCoverItem.java +++ b/src/ac/ed/lurg/landuse/LandCoverItem.java @@ -20,10 +20,8 @@ public class LandCoverItem implements RasterItem { private LandCoverTile landCoverAreas = new LandCoverTile(); private Map<CropType, Double> cropFractions = new HashMap<>(); private double totalArea; - private double unavailableFract; // due to slope + private double maxCroplandFraction; // due to slope private double protectedFraction; - private List<LandCoverType> unavailPriorityList = new ArrayList<LandCoverType>( - Arrays.asList(LandCoverType.NATURAL, LandCoverType.TIMBER_FOREST, LandCoverType.PASTURE, LandCoverType.CROPLAND)); /** Area in Mha */ public Double getLandCoverArea(LandCoverType landType) { @@ -53,12 +51,14 @@ public class LandCoverItem implements RasterItem { landCoverFract.put(landType, d); } - public double getUnavailableFract(){ - return unavailableFract; + public double getMaxCroplandArea(){ + double maxArea = maxCroplandFraction * getTotalArea(); + double cropArea = landCoverAreas.getTotalArea(LandCoverType.CROPLAND); + return Math.max(maxArea, cropArea); // In case of disagreement, trust land cover data } public void setMaxCropFraction(double maxCropFraction){ - this.unavailableFract=1.0-maxCropFraction; + this.maxCroplandFraction = maxCropFraction; } public double getProtectedFraction() { @@ -71,7 +71,6 @@ public class LandCoverItem implements RasterItem { public LandCoverTile getLandCoverTiles() { createLandCoverTiles(); - setUnavailableArea(); cleanUp(); return landCoverAreas; } @@ -126,40 +125,6 @@ public class LandCoverItem implements RasterItem { runAreaCheck(initAreas, landCoverAreas); } - private void setUnavailableArea() { - double alreadyUnavail = 0; - for (LandCoverType lcType : LandCoverType.values()) { - alreadyUnavail += landCoverAreas.getArea(lcType, LandProtectionType.UNAVAILABLE); - } - double unavailableArea = unavailableFract * totalArea; - double shortfall = Math.max(0, unavailableArea - alreadyUnavail); - if (shortfall == 0) - return; - - for (LandCoverType lcType : unavailPriorityList) { - double cArea = landCoverAreas.getArea(lcType, LandProtectionType.CONVERTIBLE); - double pArea = landCoverAreas.getArea(lcType, LandProtectionType.PROTECTED); - double totalArea = cArea + pArea; - double cProp = cArea / totalArea; - - double additionalUnavail = Math.min(shortfall, totalArea); - if (additionalUnavail > 0) { - // all additional unavailable area moved to NATURAL land cover - landCoverAreas.addArea(LandCoverType.NATURAL, LandProtectionType.UNAVAILABLE, additionalUnavail); - landCoverAreas.removeArea(lcType, LandProtectionType.CONVERTIBLE, additionalUnavail * cProp); - landCoverAreas.removeArea(lcType, LandProtectionType.PROTECTED, additionalUnavail * (1 - cProp)); - shortfall -= additionalUnavail; - } - - if (shortfall < 1e-10) - break; - } - - if (shortfall >= 1e-10) { - LogWriter.printlnWarning("LandUseItem.setUnavailableArea insufficient area to make unavailable"); - } - } - public void runAreaCheck(Map<LandCoverType, Double> lcAreas, LandCoverTile landCoverAreas) { final double THRESHOLD = 1e-7; for (LandCoverType lcType : LandCoverType.values()) { diff --git a/src/ac/ed/lurg/landuse/LandUseItem.java b/src/ac/ed/lurg/landuse/LandUseItem.java index fbe118bf6b229a827b1f23b1fddaa76e195c4f5a..4dda40832835f95184fd787d2fc4bc5acaa5e73b 100644 --- a/src/ac/ed/lurg/landuse/LandUseItem.java +++ b/src/ac/ed/lurg/landuse/LandUseItem.java @@ -7,6 +7,7 @@ import java.util.Map; import ac.ed.lurg.ModelConfig; import ac.ed.lurg.types.*; import ac.ed.lurg.utils.Interpolator; +import ac.ed.lurg.utils.LogWriter; import ac.sac.raster.InterpolatingRasterItem; public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serializable { @@ -18,6 +19,7 @@ public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serial private Map<LccKey, Double> landCoverChange = new HashMap<LccKey, Double>(); // land cover change for previous timestep private double protectedFraction; // protected fraction of total area private double forestRotationIntensity; + private double maxCroplandArea = 0; public LandUseItem() {} @@ -27,9 +29,11 @@ public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serial for (CropType crop : CropType.getCropsLessPasture()) { setCropFraction(crop, landCover.getCropFraction(crop)); } - + setMaxCroplandArea(landCover.getMaxCroplandArea()); setProtectedFraction(landCover.getProtectedFraction()); this.landCoverAreas = (landCover.getLandCoverTiles()); + } else { + maxCroplandArea = getTotalLandArea(); } forestRotationIntensity = 0.02; } @@ -41,6 +45,7 @@ public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serial landCoverAreas = (luItemToCopy.landCoverAreas); protectedFraction = (luItemToCopy.protectedFraction); forestRotationIntensity = (luItemToCopy.forestRotationIntensity); + maxCroplandArea = (luItemToCopy.maxCroplandArea); } public void overwriteLandCoverAreas(LandCoverTile landCoverAreas) { @@ -100,6 +105,21 @@ public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serial return totalArea==0 ? 0.0 : getLandCoverArea(c) / totalArea; } + public void setMaxCroplandArea(double maxCroplandArea) { + this.maxCroplandArea = maxCroplandArea; + } + + public double getMaxCroplandArea() { + return maxCroplandArea; + } + + public double getAreaSuitableForCropland() { // how much land can be converted to cropland + if (getLandCoverArea(LandCoverType.CROPLAND) > getMaxCroplandArea()) { + LogWriter.printlnWarning("Cropland exceeds maximum area!"); + } + return Math.max(0, getMaxCroplandArea() - getLandCoverArea(LandCoverType.CROPLAND)); + } + public double getSuitableArea() { double suitableArea = 0; for (LandCoverType lcType : LandCoverType.getConvertibleTypes()) {