From e883162ff644b236adf816fedb446ee3759d2d04 Mon Sep 17 00:00:00 2001 From: Peter Alexander <peter@blackhillock.co.uk> Date: Wed, 22 Jul 2015 10:59:18 +0100 Subject: [PATCH] Change values held by AreasItem --- GAMS/IntExtOpt.gms | 3 + src/ac/ed/lurg/ModelMain.java | 4 +- src/ac/ed/lurg/country/CountryAgent.java | 15 +- .../country/gams/GamsLocationOptimiser.java | 72 ++++--- .../lurg/country/gams/GamsLocationTest.java | 25 +-- .../country/gams/GamsRasterOptimiser.java | 192 ++++++++++-------- src/ac/ed/lurg/landuse/AreasItem.java | 77 ++++--- src/ac/ed/lurg/landuse/LandCoverItem.java | 8 +- src/ac/ed/lurg/landuse/LandCoverReader.java | 6 +- src/ac/ed/lurg/types/CropToDouble.java | 9 - src/ac/ed/lurg/types/CropType.java | 2 +- .../{LandDataType.java => LandCoverType.java} | 5 +- src/ac/sac/raster/RasterSet.java | 1 + 13 files changed, 238 insertions(+), 181 deletions(-) rename src/ac/ed/lurg/types/{LandDataType.java => LandCoverType.java} (79%) diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index 5cef5c68..de004b9d 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -215,6 +215,9 @@ $gdxin totalProdCost('meat') = sum(feed_crop, feedEnergy(feed_crop)) + parameter totalCropland(location); + totalCropland(location) = sum(crop_less_pasture, area.l(crop_less_pasture, location)); + display totalProdCost, totalProd, totalImportEnergy, feedEnergy, totalProdCost diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java index 711672f0..9500e56e 100644 --- a/src/ac/ed/lurg/ModelMain.java +++ b/src/ac/ed/lurg/ModelMain.java @@ -28,7 +28,7 @@ import ac.ed.lurg.output.RasterOutputer; import ac.ed.lurg.types.CropToDoubleMap; import ac.ed.lurg.types.CropType; import ac.ed.lurg.types.FertiliserRate; -import ac.ed.lurg.types.LandDataType; +import ac.ed.lurg.types.LandCoverType; import ac.ed.lurg.types.ModelFitType; import ac.ed.lurg.utils.LogWriter; import ac.ed.lurg.yield.LPJYieldResponseMapReader; @@ -281,7 +281,7 @@ public class ModelMain { String rootDir = ModelConfig.INITAL_LAND_COVER_DIR + File.separator; - for (LandDataType landType : LandDataType.values()) { + for (LandCoverType landType : LandCoverType.values()) { LandCoverReader lcReader = new LandCoverReader(initLC, landType); initLC = lcReader.getRasterDataFromFile(rootDir + landType.getFileName()); } diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java index d23dc87d..4b2507f1 100644 --- a/src/ac/ed/lurg/country/CountryAgent.java +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -15,7 +15,6 @@ import ac.ed.lurg.landuse.IrrigationCostItem; import ac.ed.lurg.landuse.LandCoverItem; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandDataType; import ac.ed.lurg.utils.LogWriter; import ac.ed.lurg.yield.YieldRaster; import ac.sac.raster.RasterKey; @@ -48,21 +47,15 @@ public class CountryAgent { private RasterSet<AreasItem> convertInitialLC(RasterSet<LandCoverItem> initialLC) { RasterSet<AreasItem> cropAreaRaster = new RasterSet<AreasItem>(initialLC.getHeaderDetails()); - for (Map.Entry<RasterKey, LandCoverItem> entry : initialLC.entrySet()) { AreasItem areasItem = new AreasItem(); RasterKey key = entry.getKey(); - LandCoverItem landCover = entry.getValue(); - - if (landCover != null) { - areasItem.setCropArea(CropType.WHEAT, landCover.getLandCover(LandDataType.CROPLAND)); // we allow free substitution between crop types so this is ok-ish - areasItem.setCropArea(CropType.PASTURE, landCover.getLandCover(LandDataType.PASTURE)); - areasItem.setLandCoverArea(LandDataType.FOREST, landCover.getLandCover(LandDataType.FOREST)); - areasItem.setLandCoverArea(LandDataType.OTHER_NATURAL, landCover.getLandCover(LandDataType.OTHER_NATURAL)); - } - + areasItem.setLandCoverAreas(entry.getValue()); + areasItem.setCropFraction(CropType.WHEAT, 0.5); // random start, better if we could get data, but free substitution between crops so not critical + areasItem.setCropFraction(CropType.MAIZE, 0.5); cropAreaRaster.put(key, areasItem); } + return cropAreaRaster; } diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index c04453ef..8bdf7f14 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -14,6 +14,8 @@ import ac.ed.lurg.landuse.Intensity; import ac.ed.lurg.landuse.IrrigationCostItem; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.utils.LazyHashMap; import ac.ed.lurg.utils.LogWriter; import ac.ed.lurg.yield.YieldResponsesItem; @@ -92,21 +94,22 @@ public class GamsLocationOptimiser { Integer locationId = entry.getKey(); String locString = Integer.toString(locationId); AreasItem areasItem = entry.getValue(); + + double suitableLand = areasItem.getSuitableLand(); + if (DEBUG) LogWriter.println(String.format(" %d %15s,\t %.1f", locationId, "suitableLand", suitableLand)); + landP.addRecord(locString).setValue(suitableLand); + + double pastureArea = areasItem.getLandCoverArea(LandCoverType.PASTURE); + if (DEBUG) LogWriter.println(String.format(" %d %15s,\t %.1f", locationId, "pastureArea", pastureArea)); + setPreviousArea(prevCropP, locString, CropType.PASTURE.getGamsName(), pastureArea); + totalAgriLand += pastureArea; - for (CropType cropType : CropType.values()) { + for (CropType cropType : CropType.getCropsLessPasture()) { double d = areasItem.getCropArea(cropType); if (DEBUG && d != 0) LogWriter.println(String.format(" %d %15s,\t %.1f", locationId, cropType.getGamsName(), d)); - - Vector<String> v = new Vector<String>(); - v.add(cropType.getGamsName()); - v.add(locString); - prevCropP.addRecord(v).setValue(d); + setPreviousArea(prevCropP, locString, cropType.getGamsName(), d); totalAgriLand += d; } - - double suitableLand = areasItem.getSuitableLand(); - if (DEBUG) LogWriter.println(String.format(" %d %15s,\t %.1f", locationId, "suitableLand", suitableLand)); - landP.addRecord(locString).setValue(suitableLand); } if (DEBUG) LogWriter.println(String.format(" Total agricultural\t %.1f", totalAgriLand)); @@ -183,6 +186,14 @@ public class GamsLocationOptimiser { addItemMapParm(inDB.addParameter("cropAdj", 1), countryInput.getCropAdjustments()); } + private void setPreviousArea(GAMSParameter prevCropP, String locString, String cropTypeString, double d) { + Vector<String> v = new Vector<String>(); + v.add(cropTypeString); + v.add(locString); + prevCropP.addRecord(v).setValue(d); + } + + @SuppressWarnings("serial") private GamsLocationOutput handleResults(GAMSDatabase outDB) { int modelStatus = (int) outDB.getParameter("ms").findRecord().getValue(); System.out.println( @@ -200,15 +211,20 @@ public class GamsLocationOptimiser { GAMSParameter parmCropAdj = outDB.getParameter("cropAdj"); GAMSParameter parmProd = outDB.getParameter("totalProd"); GAMSParameter parmProdCost = outDB.getParameter("totalProdCost"); + GAMSParameter parmCroplandArea = outDB.getParameter("totalCropland"); double totalArea = 0; double area, fertIntensity, irrigIntensity, otherIntensity = Double.NaN, feedAmount, netImport, yield, unitEnergy, cropAdj, prod, prodCost; - Map<Integer, IntensitiesItem> intensities = new HashMap<Integer, IntensitiesItem>(); - Map<Integer, AreasItem> cropAreas = new HashMap<Integer, AreasItem>(); + LazyHashMap<Integer, IntensitiesItem> intensities = new LazyHashMap<Integer, IntensitiesItem>() { + protected IntensitiesItem createValue() { return new IntensitiesItem(); } + }; + LazyHashMap<Integer, AreasItem> cropAreas = new LazyHashMap<Integer, AreasItem>() { + protected AreasItem createValue() { return new AreasItem(); } + }; + Map<CropType, CropUsageData> cropUsageData = new HashMap<CropType, CropUsageData>(); Map<CropType, Double> cropAdjs = new HashMap<CropType, Double>(); - CropType prevCropType = null; for (GAMSVariableRecord rec : varAreas) { String itemName = rec.getKeys()[0]; @@ -223,39 +239,33 @@ public class GamsLocationOptimiser { int locId = Integer.parseInt(locationName); CropType cropType = CropType.getForGamsName(itemName); - if (!cropType.equals(prevCropType)) { + if (!cropUsageData.containsKey(cropType)) { // then we must not have seen this crop type before, so need to do all non location specific stuff feedAmount = varFeedAmount.findRecord(itemName).getLevel(); netImport = cropType.isImportedCrop() ? varNetImports.findRecord(itemName).getLevel() : 0; cropAdj = getParmValue(parmCropAdj, itemName); prod = getParmValue(parmProd, itemName); prodCost = getParmValue(parmProdCost, itemName); - + cropUsageData.put(cropType, new CropUsageData(feedAmount, netImport, prod, prodCost)); cropAdjs.put(cropType, cropAdj); if (DEBUG) LogWriter.println(String.format("\n%s:\tfeedAmount= %.1f,\tnetImports= %.3f,\tprod= %.3f,\tprodCost= %.3f,\tcropAdj= %.3f", itemName, feedAmount, netImport, prod, prodCost, cropAdj)); } if (area > 0) { - if (DEBUG) LogWriter.println(String.format("\t location %s, %s:\tarea= %.1f,\tfert= %.3f,\tirrg= %.3f,\tintensity= %.3f", - locationName, itemName, area, fertIntensity, irrigIntensity, otherIntensity)); - - IntensitiesItem intensityItem = intensities.get(locId); - if (intensityItem == null) { - intensityItem = new IntensitiesItem(); - intensities.put(locId, intensityItem); - } + if (DEBUG) LogWriter.println(String.format("\t location %s, %s:\tarea= %.1f,\tfert= %.3f,\tirrg= %.3f,\tintensity= %.3f", locationName, itemName, area, fertIntensity, irrigIntensity, otherIntensity)); + IntensitiesItem intensityItem = intensities.lazyGet(locId); intensityItem.setIntensity(cropType, new Intensity(fertIntensity, irrigIntensity, otherIntensity, yield, unitEnergy)); } - AreasItem areaItem = cropAreas.get(locId); - if (areaItem == null) { - areaItem = new AreasItem(); - cropAreas.put(locId, areaItem); - } - areaItem.setCropArea(cropType, area); - totalArea += area; + double croplandArea = getParmValue(parmCroplandArea, locationName); + AreasItem areasItem = cropAreas.lazyGet(locId); + if (cropType.equals(CropType.PASTURE)) + areasItem.setLandCoverArea(LandCoverType.PASTURE, area); + else + areasItem.setLandCoverArea(LandCoverType.CROPLAND, croplandArea); // will set this multiple times, once for each arable crop, but doesn't really matter - prevCropType = cropType; + areasItem.setCropFraction(cropType, croplandArea > 0 ? area/croplandArea : 0); + totalArea += area; } netImport = varNetImports.findRecord(CropType.MEAT.getGamsName()).getLevel(); diff --git a/src/ac/ed/lurg/country/gams/GamsLocationTest.java b/src/ac/ed/lurg/country/gams/GamsLocationTest.java index 73165127..84ec9637 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationTest.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationTest.java @@ -8,7 +8,7 @@ import ac.ed.lurg.landuse.AreasItem; import ac.ed.lurg.landuse.IrrigationCostItem; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandDataType; +import ac.ed.lurg.types.LandCoverType; import ac.ed.lurg.types.YieldType; import ac.ed.lurg.yield.YieldResponsesItem; @@ -94,18 +94,19 @@ public class GamsLocationTest { AreasItem getAreaItem(double cereal, double pasture) { AreasItem dummyMap = new AreasItem(); - dummyMap.setCropArea(CropType.WHEAT, 5.0 + cereal); - dummyMap.setCropArea(CropType.MAIZE, 5.0); - dummyMap.setCropArea(CropType.RICE, 5.0); - dummyMap.setCropArea(CropType.TROPICAL_CEREALS, 5.0); - dummyMap.setCropArea(CropType.SOYBEAN, 5.0); - dummyMap.setCropArea(CropType.OILCROPS, 5.0); - dummyMap.setCropArea(CropType.PULSES, 5.0); - dummyMap.setCropArea(CropType.STARCHY_ROOTS, 5.0); - dummyMap.setCropArea(CropType.PASTURE, 5.0 + pasture); + dummyMap.setCropFraction(CropType.WHEAT, 0.2); + dummyMap.setCropFraction(CropType.MAIZE, 0.2); + dummyMap.setCropFraction(CropType.RICE, 0.1); + dummyMap.setCropFraction(CropType.TROPICAL_CEREALS, 0.1); + dummyMap.setCropFraction(CropType.SOYBEAN, 0.1); + dummyMap.setCropFraction(CropType.OILCROPS, 0.1); + dummyMap.setCropFraction(CropType.PULSES, 0.1); + dummyMap.setCropFraction(CropType.STARCHY_ROOTS, 0.1); - dummyMap.setLandCoverArea(LandDataType.FOREST, 200 - pasture - cereal); - dummyMap.setLandCoverArea(LandDataType.OTHER_NATURAL, 30); + dummyMap.setLandCoverArea(LandCoverType.PASTURE, pasture); + dummyMap.setLandCoverArea(LandCoverType.CROPLAND, cereal); + dummyMap.setLandCoverArea(LandCoverType.FOREST, 200 - pasture - cereal); + dummyMap.setLandCoverArea(LandCoverType.OTHER_NATURAL, 30); return dummyMap; } diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java index dcad1922..fac3693e 100644 --- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java @@ -1,7 +1,6 @@ package ac.ed.lurg.country.gams; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -9,12 +8,13 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import ac.ed.lurg.ModelConfig; import ac.ed.lurg.landuse.AreasItem; import ac.ed.lurg.landuse.IntensitiesItem; import ac.ed.lurg.landuse.IrrigationCostItem; -import ac.ed.lurg.types.CropToDouble; +import ac.ed.lurg.output.RasterOutputer; import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandDataType; +import ac.ed.lurg.types.LandCoverType; import ac.ed.lurg.types.YieldType; import ac.ed.lurg.utils.LazyHashMap; import ac.ed.lurg.utils.LogWriter; @@ -61,9 +61,35 @@ public class GamsRasterOptimiser { return new GamsRasterOutput(gamsOutput.getStatus(), newIntensityRaster, newAreaRaster, gamsOutput.getCommoditiesData(), gamsOutput.getCropAdjustments(), locationIdRaster); } + private RasterSet<AreasItem> createWithSameLandCovers(RasterSet<AreasItem> toCopy) { + RasterSet<AreasItem> theCopy = new RasterSet<AreasItem>(toCopy.getHeaderDetails()); + + for (Entry<RasterKey, AreasItem> entry : toCopy.entrySet()) { + AreasItem newAreasItem = new AreasItem(); + newAreasItem.setLandCoverAreas(entry.getValue()); + theCopy.put(entry.getKey(), newAreasItem); + } + + return theCopy; + } + + @SuppressWarnings("unused") + private void checkedTotalAreas(RasterSet<AreasItem> areaRaster, String comment) { + for (LandCoverType l : LandCoverType.values()) { + double total = 0; + for (AreasItem a : areaRaster.values()) { + total += a.getLandCoverArea(l); + } + + LogWriter.printlnError("Total Area " + comment + ": " + l.getName() + ": " + total); + } + } + private RasterSet<AreasItem> allocAreas(Map<Integer, ? extends AreasItem> prevAreasAgg, GamsLocationOutput gamsOutput) { - RasterSet<AreasItem> prevAreaRaster = rasterInputData.getPreviousAreas(); - RasterSet<AreasItem> newAreaRaster = new RasterSet<AreasItem>(prevAreaRaster.getHeaderDetails()); + RasterSet<AreasItem> newAreaRaster = createWithSameLandCovers(rasterInputData.getPreviousAreas()); + + //checkedTotalAreas(rasterInputData.getPreviousAreas(), "old"); + //checkedTotalAreas(newAreaRaster, "new"); for (Map.Entry<Integer, AreasItem> entry : gamsOutput.getCropAreas().entrySet()) { Integer locId = entry.getKey(); @@ -71,11 +97,13 @@ public class GamsRasterOptimiser { AreasItem prevAreaAggItem = prevAreasAgg.get(locId); Set<RasterKey> keys = mapping.get(locId); - CropToDouble cropChangeTotals = newAreaAggItem.getCropChanges(prevAreaAggItem); + //checkedTotalAreas(newAreaRaster.popSubsetForKeys(new RasterSet<AreasItem>(), keys), locId + " setset"); + + // if (DEBUG) LogWriter.println("Processing location id " + locId); - double pastureChange = cropChangeTotals.get(CropType.PASTURE); - double croplandChange = cropChangeTotals.getCroplandTotal(); + double pastureChange = newAreaAggItem.getLandCoverArea(LandCoverType.PASTURE) - prevAreaAggItem.getLandCoverArea(LandCoverType.PASTURE); + double croplandChange = newAreaAggItem.getLandCoverArea(LandCoverType.CROPLAND) - prevAreaAggItem.getLandCoverArea(LandCoverType.CROPLAND); double prevForestToNaturalFraction = prevAreaAggItem.getForestToNaturalFraction(); double pastureFromCrop = 0; @@ -84,103 +112,74 @@ public class GamsRasterOptimiser { if (pastureChange > 0 && croplandChange < 0) { pastureFromCrop = Math.min(pastureChange, -croplandChange); - pastureFromNatural = (pastureChange > -croplandChange) ? 0 : pastureChange+croplandChange; + pastureFromNatural = (pastureChange > -croplandChange) ? pastureChange+croplandChange : 0; + cropFromNatural = croplandChange + pastureFromCrop; } else if (pastureChange < 0 && croplandChange > 0) { pastureFromCrop = -Math.min(-pastureChange, croplandChange); - cropFromNatural = (croplandChange > -pastureChange) ? 0 : -(pastureChange+croplandChange); + cropFromNatural = (croplandChange > -pastureChange) ? -(pastureChange+croplandChange) : 0; + pastureFromNatural = pastureChange - pastureFromCrop; } else { pastureFromNatural = pastureChange; cropFromNatural = croplandChange; } - // allocAllAreasForAgg(newAreaRaster, prevAreaRaster, keys, pastureFromNatural); + double shortfall = 0; + shortfall += allocAllLandCrops(newAreaRaster, keys, LandCoverType.PASTURE, LandCoverType.FOREST, pastureFromNatural * prevForestToNaturalFraction); + shortfall += allocAllLandCrops(newAreaRaster, keys, LandCoverType.PASTURE, LandCoverType.OTHER_NATURAL, pastureFromNatural * (1-prevForestToNaturalFraction)); + shortfall += allocAllLandCrops(newAreaRaster, keys, LandCoverType.PASTURE, LandCoverType.CROPLAND, pastureFromCrop); + shortfall += allocAllLandCrops(newAreaRaster, keys, LandCoverType.CROPLAND, LandCoverType.FOREST, cropFromNatural * prevForestToNaturalFraction); + shortfall += allocAllLandCrops(newAreaRaster, keys, LandCoverType.CROPLAND, LandCoverType.OTHER_NATURAL, cropFromNatural * (1-prevForestToNaturalFraction)); - - double shortfall = allocAllAreasForAgg(newAreaRaster, prevAreaRaster, keys, cropChangeTotals); if (shortfall > 0.00001) LogWriter.printlnError("This should never happen, due to GAMS constraint. Not able to incorporate all changes, as not enough forest or natural areas left: " + shortfall); + + for (RasterKey key : keys) { + AreasItem newAreasRasterItem = newAreaRaster.get(key); + for (CropType crop : CropType.values()) { + newAreasRasterItem.setCropFraction(crop, newAreaAggItem.getCropFraction(crop)); + } + } } return newAreaRaster; } - - private double allocAllAreasForAgg(RasterSet<AreasItem> newAreaRaster, RasterSet<AreasItem> prevAreaRaster, Set<RasterKey> keys, CropToDouble cropChange) { - + + private double allocAllLandCrops(RasterSet<AreasItem> newAreaRaster, Set<RasterKey> keys, LandCoverType toLC, LandCoverType fromLC, double change) { + LandCoverType toType = toLC; + LandCoverType fromType = fromLC; + + // reverse direction if negative + if (change < 0) { + change = -change; + toType = fromLC; + fromType = toLC; + } + double totalShortfall = 0; Set<RasterKey> keysWithSpace = new HashSet<RasterKey>(); - CropToDouble avgCropChange = cropChange.scale(1.0/keys.size()); + double avgChange = change/keys.size(); for (RasterKey key : keys) { //if (DEBUG) LogWriter.println(" Processing raster key " + key); - AreasItem prevAreasRasterItem = prevAreaRaster.get(key); - - AreasItem newAreasRasterItem = new AreasItem(); - double shortfall = allocRasterArea(newAreasRasterItem, prevAreasRasterItem, avgCropChange); + AreasItem newAreasRasterItem = newAreaRaster.get(key); + + double shortfall = newAreasRasterItem.moveAreas(toType, fromType, avgChange); if (shortfall == 0) keysWithSpace.add(key); else totalShortfall += shortfall; - - newAreaRaster.put(key, newAreasRasterItem); } if (totalShortfall > 0 & keysWithSpace.size() > 0) { // more to allocate and some free areas to allocate into - if (DEBUG) LogWriter.println("A total area of " + totalShortfall + " is unallocated, so trying again with areas with space"); - double factor = totalShortfall / cropChange.getTotal(); - - allocAllAreasForAgg(newAreaRaster, newAreaRaster, keysWithSpace, cropChange.scale(factor)); + if (DEBUG) LogWriter.println("A total land cover of " + totalShortfall + " is unallocated, so trying again with areas with space"); + totalShortfall = allocAllLandCrops(newAreaRaster, keysWithSpace, toType, fromType, totalShortfall); } return totalShortfall; } - /** returns the net area of crop change that not been able to be allocated */ - private double allocRasterArea(AreasItem newAreasRasterItem, AreasItem prevAreasRasterItem, CropToDouble cropChange) { - - double netAddedCrop = cropChange.getTotal(); - double forestRemoved = netAddedCrop/2; - double naturalRemoved = netAddedCrop/2; - double prevForest = prevAreasRasterItem.getLandCoverArea(LandDataType.FOREST); - double prevNatural = prevAreasRasterItem.getLandCoverArea(LandDataType.OTHER_NATURAL); - - double ratioOfChanges = 1; - - if (netAddedCrop > prevForest + prevNatural) { - ratioOfChanges = (prevForest + prevNatural) / netAddedCrop; - forestRemoved = prevForest; - naturalRemoved = prevNatural; - if (DEBUG) LogWriter.println("Not able to incorporate all changes for raster location: " + netAddedCrop); - } - else if (prevForest < forestRemoved) { - forestRemoved = prevForest; - naturalRemoved = netAddedCrop - forestRemoved; - } - else if (prevNatural < naturalRemoved) { - naturalRemoved = prevNatural; - forestRemoved = netAddedCrop - naturalRemoved; - } - else { - // enough space to keep 50/50 allocation - } - - newAreasRasterItem.setLandCoverArea(LandDataType.FOREST, prevForest - forestRemoved); - newAreasRasterItem.setLandCoverArea(LandDataType.OTHER_NATURAL, prevNatural - naturalRemoved); - - for (CropType crop : CropType.values()) { - double prevCropArea = prevAreasRasterItem.getCropArea(crop); - Double d = cropChange.get(crop); - - double additionalArea = (d == null) ? 0 : d * ratioOfChanges; - // LogWriter.println(String.format("%s, %.1f", crop, additionalArea)); - - newAreasRasterItem.setCropArea(crop, prevCropArea + additionalArea); - } - - return netAddedCrop - prevForest - prevNatural; - } - private RasterSet<IntensitiesItem> allocIntensities(GamsLocationOutput gamsOutput) { RasterSet<IntensitiesItem> newIntensityRaster = new RasterSet<IntensitiesItem>(rasterInputData.getPreviousAreas().getHeaderDetails()); @@ -201,6 +200,35 @@ public class GamsRasterOptimiser { // as a first attempt only going to look at pasture and cereal yields, assuming other yields will be correlated to one or the other. YieldRaster yieldRaster = rasterInputData.getYields(); RasterSet<AreasItem> cropAreaRaster = rasterInputData.getPreviousAreas(); + + /* checkedTotalAreas(cropAreaRaster, "before"); + new RasterOutputer<AreasItem>(cropAreaRaster, "BeforeLandCover") { + @Override + public Double getValue(RasterKey location) { + AreasItem area = results.get(location); + if (area == null) + return null; + + int count = 0; + double maxLC = 0; + int maxId = 0; + for (LandCoverType l : LandCoverType.values()) { + count++; + if (maxLC < area.getLandCoverArea(l)) { + maxLC = area.getLandCoverArea(l); + maxId = count; + } + } + return (double)maxId; + } + + @Override + public int convertToPixelValue(double value) { + return (int) (1 + value * 2); + } + }.writeOutput(true); */ + + RasterSet<IrrigationCostItem> irrigCostRaster = rasterInputData.getIrrigationCost(); { @@ -247,6 +275,8 @@ public class GamsRasterOptimiser { int countFound = 0, countMissing = 0; + + for (Entry<RasterKey, YieldResponsesItem> entry : yieldRaster.entrySet()) { YieldResponsesItem yresp = entry.getValue(); RasterKey key = entry.getKey(); @@ -264,7 +294,7 @@ public class GamsRasterOptimiser { LogWriter.printlnError("GamsRasterOptimiser: Can't find cropAreas for " + key); continue; } - + IrrigationCostItem irrigCost = irrigCostRaster.get(key); int cerealCat = findCategory(wheatlDivisions, yresp.getYieldMax(CropType.WHEAT)); @@ -274,7 +304,7 @@ public class GamsRasterOptimiser { AveragingYieldResponsesItem avgYResp = aggregatedYields.lazyGet(id); AreasItem aggAreas = aggregatedAreas.lazyGet(id); IrrigationCostItem avgIrigCost = aggregatedIrrigCosts.lazyGet(id); - mapping.lazyGet(id).add(entry.getKey()); + mapping.lazyGet(id).add(key); // Irrigation cost double suitableAreaThisTime = cropAreas.getSuitableLand(); @@ -284,7 +314,7 @@ public class GamsRasterOptimiser { //LogWriter.println(id + ", " + key + ", " + avgIrigCost.getIrrigCost() + ", from " + areaSoFar + ", " + suitableAreaThisTime + ", " + irrigCost.getIrrigCost()); } - // Crop yields + // Crops yields and area fractions for (CropType crop : CropType.getNonMeatTypes()) { for (YieldType yieldType : YieldType.values()) { avgYResp.setYield(yieldType, crop, yresp.getYield(yieldType, crop)); @@ -292,11 +322,14 @@ public class GamsRasterOptimiser { double areaSoFar = aggAreas.getCropArea(crop); double areaThisTime = cropAreas.getCropArea(crop); - aggAreas.setCropArea(crop, areaSoFar + areaThisTime); + double updateCropland = (cropAreas.getLandCoverArea(LandCoverType.CROPLAND) + aggAreas.getLandCoverArea(LandCoverType.CROPLAND)); + + if (updateCropland > 0) + aggAreas.setCropFraction(crop, (areaSoFar + areaThisTime) / updateCropland); } - - // Land cover, we only need forest and other natural - for (LandDataType landType : Arrays.asList(LandDataType.FOREST, LandDataType.OTHER_NATURAL)) { + + // Land covers ares + for (LandCoverType landType : LandCoverType.values()) { double areaThisTime = cropAreas.getLandCoverArea(landType); double areaSoFar = aggAreas.getLandCoverArea(landType); aggAreas.setLandCoverArea(landType, areaSoFar + areaThisTime); @@ -306,7 +339,6 @@ public class GamsRasterOptimiser { LogWriter.println("YieldResponsesItem: " + rasterInputData.getCountryInput().getCountry() + ", countFound=" + countFound + ", countMissing=" + countMissing); - for (Map.Entry<Integer, Set<RasterKey>> e : mapping.entrySet()) { LogWriter.println(e.getKey() + " category has " + e.getValue().size() + " raster areas"); if (DEBUG) for (RasterKey key : e.getValue()) { diff --git a/src/ac/ed/lurg/landuse/AreasItem.java b/src/ac/ed/lurg/landuse/AreasItem.java index 9b85996b..e5b6f4d7 100644 --- a/src/ac/ed/lurg/landuse/AreasItem.java +++ b/src/ac/ed/lurg/landuse/AreasItem.java @@ -6,7 +6,7 @@ import java.util.Map.Entry; import ac.ed.lurg.types.CropToDouble; import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandDataType; +import ac.ed.lurg.types.LandCoverType; import ac.sac.raster.RasterItem; /* @@ -14,57 +14,84 @@ import ac.sac.raster.RasterItem; */ public class AreasItem implements RasterItem { - private Map<CropType, Double> cropAreas = new HashMap<CropType, Double>(); - private Map<LandDataType, Double> landCoverAreas = new HashMap<LandDataType, Double>(); + private Map<CropType, Double> cropFractions = new HashMap<CropType, Double>(); + private Map<LandCoverType, Double> landCoverAreas = new HashMap<LandCoverType, Double>(); + + public void setLandCoverAreas(LandCoverItem landCover) { + if (landCover != null) { + for (LandCoverType lcType : LandCoverType.values()) + landCoverAreas.put(lcType, landCover.getLandCover(lcType)); + } + } + + public void setLandCoverAreas(AreasItem otherAreasItem) { + for (Entry<LandCoverType, Double> entry : otherAreasItem.landCoverAreas.entrySet()) { + landCoverAreas.put(entry.getKey(), entry.getValue()); + } + } + + /** move areas from one land cover to another, return an residual not possible */ + public double moveAreas(LandCoverType toType, LandCoverType fromType, double changeReq) { + + double prevTo = getLandCoverArea(toType); + double prevFrom = getLandCoverArea(fromType); + double actuallyChanged = Math.min(prevFrom, changeReq); + + setLandCoverArea(toType, prevTo + actuallyChanged); + setLandCoverArea(fromType, prevFrom - actuallyChanged); + + return changeReq - actuallyChanged; + } + public double getCropArea(CropType c) { - Double d = cropAreas.get(c); + Double d = getLandCoverArea(LandCoverType.CROPLAND) * getCropFraction(c); + return d; + } + + public double getCropFraction(CropType c) { + Double d = cropFractions.get(c); return d == null ? 0.0 : d; } + - public void setCropArea(CropType c, double area) { - cropAreas.put(c, area); + public void setCropFraction(CropType c, double area) { + cropFractions.put(c, area); } - public double getLandCoverArea(LandDataType c) { + public double getLandCoverArea(LandCoverType c) { Double d = landCoverAreas.get(c); return d == null ? 0.0 : d; } - public void setLandCoverArea(LandDataType c, double d) { + public void setLandCoverArea(LandCoverType c, double d) { landCoverAreas.put(c, d); } - - public double getTotalCropIncPastureArea() { - double total = 0; - for (double d : cropAreas.values()) { - total += d; - } - return total; - } - + public double getSuitableLand() { - double d = getTotalCropIncPastureArea() + getLandCoverArea(LandDataType.FOREST) + getLandCoverArea(LandDataType.OTHER_NATURAL); + double d = 0; + for (LandCoverType l : LandCoverType.values()) + d += getLandCoverArea(l); + return d; } public double getForestToNaturalFraction() { - double forest = getLandCoverArea(LandDataType.FOREST); - double natural = forest + getLandCoverArea(LandDataType.OTHER_NATURAL); + double forest = getLandCoverArea(LandCoverType.FOREST); + double natural = forest + getLandCoverArea(LandCoverType.OTHER_NATURAL); double d = forest / natural; return d; } - public CropToDouble getCropChanges(AreasItem prevAreaAggItem) { CropToDouble changes = new CropToDouble(); - for (Entry<CropType, Double> entry : prevAreaAggItem.cropAreas.entrySet()) { - CropType crop = entry.getKey(); - double change = getCropArea(crop) - entry.getValue(); - changes.put(crop, change); + for (CropType c : CropType.getCropsLessPasture()) { + double change = getCropArea(c) - prevAreaAggItem.getCropArea(c); + changes.put(c, change); } return changes; } + } diff --git a/src/ac/ed/lurg/landuse/LandCoverItem.java b/src/ac/ed/lurg/landuse/LandCoverItem.java index 32eeb82c..13f6bc77 100644 --- a/src/ac/ed/lurg/landuse/LandCoverItem.java +++ b/src/ac/ed/lurg/landuse/LandCoverItem.java @@ -3,21 +3,21 @@ package ac.ed.lurg.landuse; import java.util.HashMap; import java.util.Map; -import ac.ed.lurg.types.LandDataType; +import ac.ed.lurg.types.LandCoverType; import ac.sac.raster.RasterItem; /** Used to hold less detailed land-cover information * This is used in the initalisation phase, after that land-use is used */ public class LandCoverItem implements RasterItem { - Map<LandDataType, Double> landcover = new HashMap<LandDataType, Double>(); + Map<LandCoverType, Double> landcover = new HashMap<LandCoverType, Double>(); /** Area in Mha */ - public Double getLandCover(LandDataType landType) { + public Double getLandCover(LandCoverType landType) { return landcover.get(landType); } - public void setLandCover(LandDataType landType, double d) { + public void setLandCover(LandCoverType landType, double d) { landcover.put(landType, d); } diff --git a/src/ac/ed/lurg/landuse/LandCoverReader.java b/src/ac/ed/lurg/landuse/LandCoverReader.java index 8fbd6133..11e2e761 100644 --- a/src/ac/ed/lurg/landuse/LandCoverReader.java +++ b/src/ac/ed/lurg/landuse/LandCoverReader.java @@ -1,14 +1,14 @@ package ac.ed.lurg.landuse; -import ac.ed.lurg.types.LandDataType; +import ac.ed.lurg.types.LandCoverType; import ac.sac.raster.AbstractRasterReader; import ac.sac.raster.RasterSet; public class LandCoverReader extends AbstractRasterReader<LandCoverItem> { - private LandDataType landType; + private LandCoverType landType; - public LandCoverReader (RasterSet<LandCoverItem> dataset, LandDataType landType) { + public LandCoverReader (RasterSet<LandCoverItem> dataset, LandCoverType landType) { super(dataset); this.landType = landType; } diff --git a/src/ac/ed/lurg/types/CropToDouble.java b/src/ac/ed/lurg/types/CropToDouble.java index b8f1176c..ada35b77 100644 --- a/src/ac/ed/lurg/types/CropToDouble.java +++ b/src/ac/ed/lurg/types/CropToDouble.java @@ -22,13 +22,4 @@ public class CropToDouble extends HashMap<CropType, Double> { return total; } - - public double getCroplandTotal() { - double total = 0; - for (CropType c : CropType.getCropsLessPasture()) - total += get(c); - - return total; - - } } diff --git a/src/ac/ed/lurg/types/CropType.java b/src/ac/ed/lurg/types/CropType.java index 85c13bb3..d42471d8 100644 --- a/src/ac/ed/lurg/types/CropType.java +++ b/src/ac/ed/lurg/types/CropType.java @@ -40,7 +40,7 @@ public enum CropType { Collection<CropType> comms = new HashSet<CropType>(); for (CropType c : values()) - if (c.importedCrop || !c.isMeat) + if (c.importedCrop && !c.isMeat) comms.add(c); return comms; diff --git a/src/ac/ed/lurg/types/LandDataType.java b/src/ac/ed/lurg/types/LandCoverType.java similarity index 79% rename from src/ac/ed/lurg/types/LandDataType.java rename to src/ac/ed/lurg/types/LandCoverType.java index 9c085949..6908a9d7 100644 --- a/src/ac/ed/lurg/types/LandDataType.java +++ b/src/ac/ed/lurg/types/LandCoverType.java @@ -1,8 +1,7 @@ package ac.ed.lurg.types; -public enum LandDataType { +public enum LandCoverType { -// LAND ("land"), FOREST ("forest"), OTHER_NATURAL ("otherNatural"), CROPLAND ("cropland"), @@ -10,7 +9,7 @@ public enum LandDataType { private String name; - LandDataType(String name) { + LandCoverType(String name) { this.name = name; } diff --git a/src/ac/sac/raster/RasterSet.java b/src/ac/sac/raster/RasterSet.java index 768c30e7..4537e751 100755 --- a/src/ac/sac/raster/RasterSet.java +++ b/src/ac/sac/raster/RasterSet.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import ac.ed.lurg.landuse.AreasItem; import ac.ed.lurg.utils.LogWriter; /* Class which holds raster data. The generics used to defines the type of raster data held. -- GitLab