diff --git a/data/carbon_demand.csv b/data/carbon_demand.csv index ab16fdbfcfcae963c388c23550cbfde7342b57e6..9a2bddbb8c292bb5bce2196c61712fc849f347d6 100644 --- a/data/carbon_demand.csv +++ b/data/carbon_demand.csv @@ -1,22 +1,22 @@ year,value 2000,0 2005,0 -2010,25 -2015,31 -2020,39 -2025,48 -2030,61 -2035,76 -2040,95 -2045,119 -2050,149 -2055,178 -2060,211 -2065,244 -2070,279 -2075,312 -2080,343 -2085,371 -2090,393 -2095,409 -2100,417 +2010,20 +2015,20 +2020,26 +2025,32 +2030,40 +2035,50 +2040,63 +2045,79 +2050,99 +2055,118 +2060,140 +2065,162 +2070,186 +2075,208 +2080,228 +2085,247 +2090,262 +2095,272 +2100,278 diff --git a/data/price_caps.csv b/data/price_caps.csv new file mode 100644 index 0000000000000000000000000000000000000000..af8165a71dec44c7a3d4dd59de456d319d8143cf --- /dev/null +++ b/data/price_caps.csv @@ -0,0 +1,14 @@ +Category,Item,MinPrice,MaxPrice +crop,monogastrics,0.005,1 +crop,ruminants,0.005,1 +crop,wheat,0.005,1 +crop,maize,0.005,1 +crop,rice,0.005,1 +crop,oilcrops,0.005,1 +crop,pulses,0.005,1 +crop,starchyRoots,0.005,1 +crop,fruitveg,0.005,1 +crop,sugar,0.005,1 +crop,energycrops,0.005,1 +wood,wood,0.005,1 +carbon,carbon,0.005,1 diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java index f70325ccd85fad9a64e2ba731c8cd558f5fd6063..8d172a35465d21a042a5c83ea3dbd3d223b7554f 100644 --- a/src/ac/ed/lurg/InternationalMarket.java +++ b/src/ac/ed/lurg/InternationalMarket.java @@ -15,6 +15,7 @@ import java.util.Map.Entry; import ac.ed.lurg.country.AbstractCountryAgent; import ac.ed.lurg.country.GlobalPrice; +import ac.ed.lurg.country.PriceCapManager; import ac.ed.lurg.country.StockReader; import ac.ed.lurg.landuse.CarbonUsageData; import ac.ed.lurg.landuse.CropUsageData; @@ -31,6 +32,7 @@ public class InternationalMarket { private GlobalPrice carbonPrice; private GlobalPrice woodPrice; private PriceShockManager priceShockManager; + private PriceCapManager priceCapManager; @SuppressWarnings("unchecked") public InternationalMarket() { @@ -63,6 +65,7 @@ public class InternationalMarket { } priceShockManager = new PriceShockManager(); + priceCapManager = new PriceCapManager(); } public Map<CropType, GlobalPrice> getWorldPrices() { @@ -188,6 +191,9 @@ public class InternationalMarket { if (adjustedTPrice.getStockLevel() < 0) LogWriter.println("Global stocks are below zero wood, " + timestep.getYear()); woodPrice = adjustedTPrice; + + // Cap prices + capPrices(); } @@ -289,6 +295,17 @@ public class InternationalMarket { LogWriter.println(String.format("No negative stocks found")); return false; } + + public void capPrices() { + Map<CropType, GlobalPrice> cappedCropPrices = priceCapManager.capCropPrices(worldPrices); + worldPrices.putAll(cappedCropPrices); + + GlobalPrice updatedWoodPrice = priceCapManager.capWoodPrices(woodPrice); + woodPrice = updatedWoodPrice; + + GlobalPrice updatedCarbonPrice = priceCapManager.capCarbonPrices(carbonPrice); + carbonPrice = updatedCarbonPrice; + } /* public double findMaxPriceDiff(Map<CropType, Double> previousExportPrices) { if (previousExportPrices == null) diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index efe7d7f74e77a017b7ac4bc43a17e8e011912ff5..d744ed08efc4f133e3ce17b39f84ba88a5acb8db 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -248,14 +248,10 @@ public class ModelConfig { // Wood/carbon data public static final String FOREST_DIR = SPATIAL_DATA_DIR + File.separator + "forestry"; public static final String WOOD_AND_CARBON_DIR = getProperty("WOOD_AND_CARBON_DIR"); - //public static final String CARBON_FLUX_FILE = FOREST_DIR + File.separator + "carbon_flux_"; public static final String WOOD_YIELD_FRST_TO_AGRI_FILENAME = "wood_yield_forest_to_agri.dat"; public static final String WOOD_YIELD_FRST_TO_FRST_FILENAME = "wood_yield_forest_to_forest.dat"; public static final String WOOD_YIELD_NTRL_TO_AGRI_FILENAME = "wood_yield_ntrl_to_agri.dat"; public static final String WOOD_YIELD_NTRL_TO_FRST_FILENAME = "wood_yield_ntrl_to_forest.dat"; - public static final String CARBON_LUC_FILENAME = "carbon_luc.out"; - public static final String CARBON_NEE_FILENAME = "carbon_nee.out"; - public static final String NATURAL_FOREST_GROWTH_FILENAME = "growth_natural.out"; public static final String LAND_COVER_AGE_DIST_FILENAME = SPATIAL_DATA_DIR + File.separator + "land_cover_age_dist.txt"; public static final int LAND_COVER_INIT_AGE_GROUP_SIZE = getIntProperty("CARBON_WOOD_AGE_CLASSES", 10); // years public static final int WOOD_AND_CARBON_TIMESTEP_SIZE = getIntProperty("WOOD_AND_CARBON_TIMESTEP_SIZE", 20); // years @@ -315,6 +311,9 @@ public class ModelConfig { // Import export limits public static final double ANNUAL_MAX_IMPORT_CHANGE = getDoubleProperty("ANNUAL_MAX_IMPORT_CHANGE", 0.05); public static final double MAX_IMPORT_CHANGE = getDoubleProperty("MAX_IMPORT_CHANGE", ANNUAL_MAX_IMPORT_CHANGE*TIMESTEP_SIZE); + + // Price caps + public static final String PRICE_CAP_FILE = DATA_DIR + File.separator + "price_caps.csv"; // Fertiliser application rates in kg/ha public static final double MIN_FERT_AMOUNT = getDoubleProperty("MIN_FERT_AMOUNT", 0.0); diff --git a/src/ac/ed/lurg/carbon/YearAgeKey.java b/src/ac/ed/lurg/carbon/YearAgeKey.java deleted file mode 100644 index 8f925d2fcecd7a812afbfc31e1ba935851806a8c..0000000000000000000000000000000000000000 --- a/src/ac/ed/lurg/carbon/YearAgeKey.java +++ /dev/null @@ -1,37 +0,0 @@ -package ac.ed.lurg.carbon; - -public class YearAgeKey { - int year; - int age; - - public YearAgeKey(int year, int age) { - super(); - this.year = year; - this.age = age; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + age; - result = prime * result + year; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - YearAgeKey other = (YearAgeKey) obj; - if (age != other.age) - return false; - if (year != other.year) - return false; - return true; - } -} diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java index 95e088896d9794aa3e9848f06a7fb59c83451b79..a89b12d41533b4bbe5f3bfbd85ef9c357c9179ff 100644 --- a/src/ac/ed/lurg/country/CountryAgent.java +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -64,7 +64,7 @@ public class CountryAgent extends AbstractCountryAgent { return yieldClusters; } - private RasterSet<IntegerRasterItem> calcYieldClusters(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces) { + private RasterSet<IntegerRasterItem> calcYieldClusters(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces, RasterSet<WoodYieldItem> woodYieldData) { LogWriter.println("calcYieldClusters: for " + ModelConfig.NUM_YIELD_CLUSTERS + " clusters"); @@ -80,7 +80,9 @@ public class CountryAgent extends AbstractCountryAgent { double totalLand = luItem.getTotalLandCoverArea()-luItem.getLandCoverArea(LandCoverType.URBAN); double protectedAreaFrac = (totalLand <= 0) ? 0.0 : luItem.getTotalLandCoverArea(LandProtectionType.PROTECTED) / totalLand; - clusteringPoints.add(new YieldClusterPoint(key, yieldresp, irrigItem, protectedAreaFrac)); + WoodYieldItem wyItem = woodYieldData.get(key); + + clusteringPoints.add(new YieldClusterPoint(key, yieldresp, irrigItem, protectedAreaFrac, wyItem)); } } @@ -130,7 +132,7 @@ public class CountryAgent extends AbstractCountryAgent { } else { if (yieldClusters==null) { - yieldClusters = calcYieldClusters(irrigData, countryYieldSurfaces); // this should only be on the first timestep + yieldClusters = calcYieldClusters(irrigData, countryYieldSurfaces, woodYieldData); // this should only be on the first timestep } // optimize areas and intensity diff --git a/src/ac/ed/lurg/country/PriceCapManager.java b/src/ac/ed/lurg/country/PriceCapManager.java new file mode 100644 index 0000000000000000000000000000000000000000..7eaab58cad7b9e25af8d13e4c03d5efc358ec64f --- /dev/null +++ b/src/ac/ed/lurg/country/PriceCapManager.java @@ -0,0 +1,105 @@ +package ac.ed.lurg.country; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.PriceCapType; +import ac.ed.lurg.utils.LogWriter; + +public class PriceCapManager { + + private static final int CATEGORY_COL = 0; + private static final int ITEM_COL = 1; + private static final int MIN_PRICE_COL = 2; + private static final int MAX_PRICE_COL = 3; + + private Map<CropType, Map<PriceCapType, Double>> cropPriceCaps = new HashMap<CropType, Map<PriceCapType, Double>>(); + private Map<PriceCapType, Double> woodPriceCaps = new HashMap<PriceCapType, Double>(); + private Map<PriceCapType, Double> carbonPriceCaps = new HashMap<PriceCapType, Double>(); + + public PriceCapManager() { + readPriceCaps(); + } + + private void readPriceCaps() { + String filename = ModelConfig.PRICE_CAP_FILE; + try { + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + String category, item; + double minPrice, maxPrice; + reader.readLine(); // header + + while ((line=reader.readLine()) != null) { + String[] tokens = line.split(","); + + if (tokens.length < 4) + LogWriter.printlnError("Too few columns in " + filename + ", " + line); + + category = tokens[CATEGORY_COL]; + item = tokens[ITEM_COL]; + minPrice = Double.valueOf(tokens[MIN_PRICE_COL]); + maxPrice = Double.valueOf(tokens[MAX_PRICE_COL]); + + switch(category) { + case "crop": + CropType cropType = CropType.getForGamsName(item); + Map<PriceCapType, Double> priceCapMap = new HashMap<PriceCapType, Double>(); + priceCapMap.put(PriceCapType.MIN_PRICE, minPrice); + priceCapMap.put(PriceCapType.MAX_PRICE, maxPrice); + cropPriceCaps.put(cropType, priceCapMap); + break; + case "wood": + woodPriceCaps.put(PriceCapType.MIN_PRICE, minPrice); + woodPriceCaps.put(PriceCapType.MAX_PRICE, maxPrice); + break; + case "carbon": + carbonPriceCaps.put(PriceCapType.MIN_PRICE, minPrice); + carbonPriceCaps.put(PriceCapType.MAX_PRICE, maxPrice); + break; + } + + } + reader.close(); + + } catch (Exception e) { + LogWriter.printlnError("Failed in reading price caps data"); + LogWriter.print(e); + } + } + + private double calcCappedPriceFactor(double price, double minPrice, double maxPrice) { + double cappedPrice = Math.max(Math.min(price, maxPrice), minPrice); + double factor = cappedPrice / price; + return factor; + } + + public Map<CropType, GlobalPrice> capCropPrices(Map<CropType, GlobalPrice> cropPrices) { + Map<CropType, GlobalPrice> updatedPrices = new HashMap<CropType, GlobalPrice>(); + for (CropType cropType : cropPrices.keySet()) { + GlobalPrice prevPrice = cropPrices.get(cropType); + double factor = calcCappedPriceFactor(prevPrice.getExportPrice(), cropPriceCaps.get(cropType).get(PriceCapType.MIN_PRICE), + cropPriceCaps.get(cropType).get(PriceCapType.MAX_PRICE)); + GlobalPrice updatedPrice = prevPrice.createPriceAdjustedByFactor(factor); + updatedPrices.put(cropType, updatedPrice); + } + return updatedPrices; + } + + public GlobalPrice capWoodPrices(GlobalPrice price) { + double factor = calcCappedPriceFactor(price.getExportPrice(), woodPriceCaps.get(PriceCapType.MIN_PRICE), woodPriceCaps.get(PriceCapType.MAX_PRICE)); + GlobalPrice updatedPrice = price.createPriceAdjustedByFactor(factor); + return updatedPrice; + } + + public GlobalPrice capCarbonPrices(GlobalPrice price) { + double factor = calcCappedPriceFactor(price.getExportPrice(), carbonPriceCaps.get(PriceCapType.MIN_PRICE), carbonPriceCaps.get(PriceCapType.MAX_PRICE)); + GlobalPrice updatedPrice = price.createPriceAdjustedByFactor(factor); + return updatedPrice; + } + +} diff --git a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java index e64257e2822511e2390fe21ca0838407fc56f95e..557317639cb0acba52697b6f686d3c7cce54e4c8 100644 --- a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java +++ b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java @@ -81,6 +81,7 @@ public abstract class AbstractSSPDemandManager extends AbstractDemandManager { abstract Map<CommodityType, Double> getFoodDemandMap(SingleCountry c, int year, double gdpPc, double population, Map<CommodityType, Double> prices, double usaGdpPc, boolean outputGamsDemand); protected Map<WoodType, Double> getWoodDemand(SingleCountry country, int year) { + SspData baseSd = sspManager.get(ssp_scenario, ModelConfig.BASE_YEAR, country); SspData sd = sspManager.get(ssp_scenario, year, country); @@ -103,6 +104,10 @@ public abstract class AbstractSSPDemandManager extends AbstractDemandManager { abstract Map<WoodType, Double> getWoodDemandMap(SingleCountry country, double gdpPc, double baseGdpPc, double population, double basePopulation); protected double getCarbonDemand(SingleCountry country, int year) { + + if (!ModelConfig.IS_CARBON_ON) + return 0; + SspData sd = sspManager.get(ssp_scenario, year, country); if (sd == null) { LogWriter.printlnWarning(String.format("No ssp for %s, baseYr:%d, year:%d, %s, sd:%s. Skipping", ssp_scenario, ModelConfig.BASE_YEAR, year, country.getCountryName(), sd)); diff --git a/src/ac/ed/lurg/types/PriceCapType.java b/src/ac/ed/lurg/types/PriceCapType.java new file mode 100644 index 0000000000000000000000000000000000000000..185394000c06ded7b98973c838fc6dc3b4d331c5 --- /dev/null +++ b/src/ac/ed/lurg/types/PriceCapType.java @@ -0,0 +1,26 @@ +package ac.ed.lurg.types; + +import ac.ed.lurg.utils.LogWriter; + +public enum PriceCapType { + + MAX_PRICE("maxPrice"), + MIN_PRICE("minPrice"); + + private String name; + + private PriceCapType(String name) { + this.name = name; + } + + public static PriceCapType findByName(String priceCap){ + for(PriceCapType p : values()){ + if( p.name.equals(priceCap)){ + return p; + } + } + + LogWriter.printlnError("No PriceType found for " + priceCap); + return null; + } +} diff --git a/src/ac/ed/lurg/yield/YieldClusterPoint.java b/src/ac/ed/lurg/yield/YieldClusterPoint.java index 710e9ce7fcea4eb9690944ebf5b4d2abd44467a9..159faf71abfe8f0d6718e0bd17af41b8ad401252 100644 --- a/src/ac/ed/lurg/yield/YieldClusterPoint.java +++ b/src/ac/ed/lurg/yield/YieldClusterPoint.java @@ -3,8 +3,11 @@ package ac.ed.lurg.yield; import java.util.Arrays; import java.util.Collection; +import ac.ed.lurg.carbon.CarbonFluxItem; +import ac.ed.lurg.forestry.WoodYieldItem; import ac.ed.lurg.landuse.IrrigationItem; import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; import ac.ed.lurg.types.YieldType; import ac.ed.lurg.utils.cluster.ClusteringPoint; import ac.sac.raster.RasterKey; @@ -19,6 +22,7 @@ public class YieldClusterPoint implements ClusteringPoint<String> { private final static String MAIZE_MAX = "maizeMax"; private final static String IRRIG_WATER_AVAL = "irrigWaterAval"; private final static String PROTECTED_AREA = "protectedArea"; + private final static String FOREST_YIELD = "forestYield"; private RasterKey rasterKey; private double wheatMin; @@ -29,8 +33,9 @@ public class YieldClusterPoint implements ClusteringPoint<String> { private double pasture; private double irrigWaterAval; private double protectedArea; + private double forestYield; - public YieldClusterPoint(RasterKey rasterKey, YieldResponsesItem yields, IrrigationItem irrigItem, double protectedArea) { + public YieldClusterPoint(RasterKey rasterKey, YieldResponsesItem yields, IrrigationItem irrigItem, double protectedArea, WoodYieldItem woodYields) { this.rasterKey = rasterKey; // not sure if we be better to get a reference to the YieldResponsesItem, rather than caching these values? @@ -42,6 +47,7 @@ public class YieldClusterPoint implements ClusteringPoint<String> { this.maizeMax = yields.getYield(YieldType.FERT_MAX_IRRIG_MAX, CropType.MAIZE); this.irrigWaterAval = (irrigItem==null) ? 0.0 :irrigItem.getIrrigConstraint(); this.protectedArea = protectedArea; + this.forestYield = (woodYields==null) ? 0.0 : woodYields.getYieldAtRotation(); } public RasterKey getRasterKey() { @@ -64,6 +70,7 @@ public class YieldClusterPoint implements ClusteringPoint<String> { case MAIZE_MAX: return maizeMax; case IRRIG_WATER_AVAL: return irrigWaterAval; case PROTECTED_AREA: return protectedArea; + case FOREST_YIELD: return forestYield; default: throw new RuntimeException("YieldClusterPoint.getClusteringValue: got unknown value " + key); } @@ -71,7 +78,7 @@ public class YieldClusterPoint implements ClusteringPoint<String> { @Override public Collection<String> getAllClusteringKeys() { - return Arrays.asList(PASTURE, WHEAT_MIN, WHEAT_MAX, MAIZE_MIN, MAIZE_MAX, IRRIG_WATER_AVAL, PROTECTED_AREA); + return Arrays.asList(PASTURE, WHEAT_MIN, WHEAT_MAX, MAIZE_MIN, MAIZE_MAX, IRRIG_WATER_AVAL, PROTECTED_AREA, FOREST_YIELD); } public String toString() {