diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index 2a0a6d8f41d9875d59ca41bf487b793730403e8e..70b675331add4d9600af1abcf48c7243eec10dbc 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -438,4 +438,4 @@ $gdxin totalCropArea(crop) = sum(location, cropArea.L(crop, location)); rotationPeriod(location) = 1 / rotationIntensity.L(location); irrigWaterWithdrawn(crop, location) = irrigMaxRate(crop, location) * irrigI.L(crop, location) * cropArea.L(crop, location) * 0.01; - fertiliserUsed(crop, location) = fertI.L(crop, location) * cropArea.L(crop, location) / 1e6; + fertiliserUsed(crop, location) = fertI.L(crop, location) * cropArea.L(crop, location); diff --git a/data/carbon_demand.csv b/data/carbon_demand.csv new file mode 100644 index 0000000000000000000000000000000000000000..614c25e0373524a71394c0ffc1fdec55eaf4a116 --- /dev/null +++ b/data/carbon_demand.csv @@ -0,0 +1,82 @@ +Year,Demand_Mt,Price +2020,15,0.03 +2021,27,0.03 +2022,40,0.03 +2023,52,0.03 +2024,64,0.03 +2025,77,0.03 +2026,89,0.03 +2027,101,0.03 +2028,114,0.03 +2029,126,0.03 +2030,138,0.03 +2031,150,0.03 +2032,163,0.03 +2033,175,0.03 +2034,187,0.03 +2035,200,0.03 +2036,212,0.03 +2037,224,0.03 +2038,237,0.03 +2039,249,0.03 +2040,261,0.03 +2041,274,0.03 +2042,286,0.03 +2043,298,0.03 +2044,311,0.03 +2045,323,0.03 +2046,335,0.03 +2047,347,0.03 +2048,360,0.03 +2049,372,0.03 +2050,384,0.03 +2051,397,0.03 +2052,409,0.03 +2053,421,0.03 +2054,434,0.03 +2055,446,0.03 +2056,458,0.03 +2057,471,0.03 +2058,483,0.03 +2059,495,0.03 +2060,508,0.03 +2061,520,0.03 +2062,532,0.03 +2063,544,0.03 +2064,557,0.03 +2065,569,0.03 +2066,581,0.03 +2067,594,0.03 +2068,606,0.03 +2069,618,0.03 +2070,631,0.03 +2071,643,0.03 +2072,655,0.03 +2073,668,0.03 +2074,680,0.03 +2075,692,0.03 +2076,705,0.03 +2077,717,0.03 +2078,729,0.03 +2079,741,0.03 +2080,754,0.03 +2081,766,0.03 +2082,778,0.03 +2083,791,0.03 +2084,803,0.03 +2085,815,0.03 +2086,828,0.03 +2087,840,0.03 +2088,852,0.03 +2089,865,0.03 +2090,877,0.03 +2091,889,0.03 +2092,902,0.03 +2093,914,0.03 +2094,926,0.03 +2095,938,0.03 +2096,951,0.03 +2097,963,0.03 +2098,975,0.03 +2099,988,0.03 +2100,1000,0.03 \ No newline at end of file diff --git a/data/carbon_options.csv b/data/carbon_options.csv new file mode 100644 index 0000000000000000000000000000000000000000..ac5ee9a9465b6c0193d6cbec07e6312e20fc0b77 --- /dev/null +++ b/data/carbon_options.csv @@ -0,0 +1,26 @@ +From,To,Enabled +carbonForest,carbonForest,FALSE +carbonForest,cropland,FALSE +carbonForest,natural,FALSE +carbonForest,pasture,FALSE +carbonForest,timberForest,FALSE +cropland,carbonForest,TRUE +cropland,cropland,FALSE +cropland,natural,FALSE +cropland,pasture,FALSE +cropland,timberForest,FALSE +natural,carbonForest,FALSE +natural,cropland,FALSE +natural,natural,FALSE +natural,pasture,FALSE +natural,timberForest,FALSE +pasture,carbonForest,TRUE +pasture,cropland,FALSE +pasture,natural,FALSE +pasture,pasture,FALSE +pasture,timberForest,FALSE +timberForest,carbonForest,FALSE +timberForest,cropland,FALSE +timberForest,natural,FALSE +timberForest,pasture,FALSE +timberForest,timberForest,FALSE \ No newline at end of file diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java index 11b67d683674bdd34a31ea1b89333577651adbc2..f2389ecd83e8606950d80adac9a77c7cae8abda0 100644 --- a/src/ac/ed/lurg/InternationalMarket.java +++ b/src/ac/ed/lurg/InternationalMarket.java @@ -6,17 +6,14 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; 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.demand.CarbonDemandManager; import ac.ed.lurg.landuse.CarbonUsageData; import ac.ed.lurg.landuse.CropUsageData; import ac.ed.lurg.landuse.WoodUsageData; @@ -33,8 +30,8 @@ public class InternationalMarket { private Map<WoodType, GlobalPrice> woodPrices; private PriceShockManager priceShockManager; private PriceCapManager priceCapManager; - - @SuppressWarnings("unchecked") + private CarbonDemandManager carbonDemandManager; + public InternationalMarket() { if(ModelConfig.IS_CALIBRATION_RUN) { worldPrices = new HashMap<CropType, GlobalPrice>(); @@ -74,6 +71,7 @@ public class InternationalMarket { priceShockManager = new PriceShockManager(); priceCapManager = new PriceCapManager(); + if (ModelConfig.IS_CARBON_ON) carbonDemandManager = new CarbonDemandManager(); // already in AbstractDemandManager } public Map<CropType, GlobalPrice> getWorldPrices() { @@ -152,58 +150,75 @@ public class InternationalMarket { } // Update carbon price - double totalCarbonImport = 0; - double totalCarbonExport = 0; - double totalCarbonSequestered = 0; + if (ModelConfig.IS_CARBON_ON) { + double totalCarbonImport = 0; + double totalCarbonExport = 0; + double totalCarbonSequestered = 0; + + for (AbstractCountryAgent ca : countryAgents) { + CarbonUsageData carbonUsage = ca.getCarbonUsageData(); + totalCarbonSequestered += carbonUsage.getCarbonCredits(); + double netCarbonFlux = carbonUsage.getNetCarbonImport(); + if (netCarbonFlux >= 0) + totalCarbonImport += netCarbonFlux; + else + totalCarbonExport -= netCarbonFlux; + } + + // Countries not importing explicitly so making imports equal global demand + if (ModelConfig.CARBON_DEMAND_METHOD.equals("global")) { + totalCarbonImport = carbonDemandManager.getGlobalCarbonDemand(timestep.getYear()); + } + + totalCarbonSequestered = Math.max(totalCarbonSequestered, 0.0000001); // avoid division by 0 + GlobalPrice prevCPrice = carbonPrice; + LogWriter.println(timestep.getYear() + " Updating carbon price", 2); + GlobalPrice adjustedCPrice; + + if (ModelConfig.CARBON_DEMAND_METHOD.equals("price")) { // price set exogenously + double newPrice = carbonDemandManager.getGlobalCarbonPrice(timestep.getYear()); + adjustedCPrice = prevCPrice.createWithSetPrice(newPrice, totalCarbonImport, totalCarbonExport, timestep, totalCarbonSequestered); + } else { + adjustedCPrice = prevCPrice.createWithUpdatedMarketPrices(totalCarbonImport, totalCarbonExport, timestep, + totalCarbonSequestered, false, ModelConfig.DEFAULT_STOCK_USE_RATIO); + } + + LogWriter.println( String.format("Price for carbon updated from %s \nto %s \n", prevCPrice, adjustedCPrice), 2); + if (adjustedCPrice.getStockLevel() < 0) + LogWriter.println("Global stocks are below zero carbon, " + timestep.getYear(), 2); + carbonPrice = adjustedCPrice; - for (AbstractCountryAgent ca : countryAgents) { - CarbonUsageData carbonUsage = ca.getCarbonUsageData(); - totalCarbonSequestered += carbonUsage.getCarbonCredits(); - double netCarbonFlux = carbonUsage.getNetCarbonImport(); - if (netCarbonFlux >= 0) - totalCarbonImport += netCarbonFlux; - else - totalCarbonExport -= netCarbonFlux; } - - totalCarbonSequestered = Math.max(totalCarbonSequestered, 0.0000001); // avoid division by 0 - GlobalPrice prevCPrice = carbonPrice; - LogWriter.println(timestep.getYear() + " Updating carbon price", 2); - GlobalPrice adjustedCPrice = prevCPrice.createWithUpdatedMarketPrices(totalCarbonImport, totalCarbonExport, timestep, - totalCarbonSequestered, false, ModelConfig.DEFAULT_STOCK_USE_RATIO); - LogWriter.println( String.format("Price for carbon updated from %s to %s \n", prevCPrice, adjustedCPrice), 2); - if (adjustedCPrice.getStockLevel() < 0) - LogWriter.println("Global stocks are below zero carbon, " + timestep.getYear(), 2); - carbonPrice = adjustedCPrice; - - // Update timber price - for (WoodType woodType : WoodType.values()) { - double totalWoodImport = 0; - double totalWoodExport = 0; - double totalWoodProduction = 0; - for (AbstractCountryAgent ca : countryAgents) { - WoodUsageData woodUsage = ca.getWoodUsageData().get(woodType); - totalWoodProduction += woodUsage.getProduction(); - double netImport = woodUsage.getNetImport(); - if (netImport >= 0) { - totalWoodImport += netImport; - } else { - totalWoodExport += -netImport; + // Update timber price + if (ModelConfig.IS_FORESTRY_ON) { + for (WoodType woodType : WoodType.values()) { + double totalWoodImport = 0; + double totalWoodExport = 0; + double totalWoodProduction = 0; + + for (AbstractCountryAgent ca : countryAgents) { + WoodUsageData woodUsage = ca.getWoodUsageData().get(woodType); + totalWoodProduction += woodUsage.getProduction(); + double netImport = woodUsage.getNetImport(); + if (netImport >= 0) { + totalWoodImport += netImport; + } else { + totalWoodExport += -netImport; + } } + totalWoodProduction = Math.max(totalWoodProduction, 0.0000001); // avoid division by 0 + GlobalPrice prevTPrice = woodPrices.get(woodType); + LogWriter.println(timestep.getYear() + " Updating " + woodType.getName() + " price", 2); + GlobalPrice adjustedTPrice = prevTPrice.createWithUpdatedMarketPrices(totalWoodImport, totalWoodExport, + timestep, totalWoodProduction, true, ModelConfig.DEFAULT_STOCK_USE_RATIO); + LogWriter.println( String.format("Price for wood updated from %s \nto %s \n", prevTPrice, adjustedTPrice), 2); + if (adjustedTPrice.getStockLevel() < 0) + LogWriter.println("Global stocks are below zero wood, " + timestep.getYear(), 2); + woodPrices.put(woodType, adjustedTPrice); } - totalWoodProduction = Math.max(totalWoodProduction, 0.0000001); // avoid division by 0 - GlobalPrice prevTPrice = woodPrices.get(woodType); - LogWriter.println(timestep.getYear() + " Updating " + woodType.getName() + " price", 2); - GlobalPrice adjustedTPrice = prevTPrice.createWithUpdatedMarketPrices(totalWoodImport, totalWoodExport, - timestep, totalWoodProduction, true, ModelConfig.DEFAULT_STOCK_USE_RATIO); - LogWriter.println( String.format("Price for wood updated from %s to %s \n", prevTPrice, adjustedTPrice), 2); - if (adjustedTPrice.getStockLevel() < 0) - LogWriter.println("Global stocks are below zero wood, " + timestep.getYear(), 2); - woodPrices.put(woodType, adjustedTPrice); } - // Cap prices capPrices(); diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index bb165cc0df648b32eba3668e1225a4bca503788d..22a137dc7a5ed136bbf46d79c8180e00d47f68d5 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -425,10 +425,11 @@ public class ModelConfig { public static final int BASE_YEAR = getIntProperty("BASE_YEAR", 2020); // Import export limits - public static final double ANNUAL_MAX_IMPORT_CHANGE = getDoubleProperty("ANNUAL_MAX_IMPORT_CHANGE", 0.5); // Loose constraint as we have TRADE_ADJUSTMENT_COST_RATE + public static final double ANNUAL_MAX_IMPORT_CHANGE = getDoubleProperty("ANNUAL_MAX_IMPORT_CHANGE", 1.0); // Loose constraint as we have TRADE_ADJUSTMENT_COST_RATE public static final double MAX_IMPORT_CHANGE = getDoubleProperty("MAX_IMPORT_CHANGE", ANNUAL_MAX_IMPORT_CHANGE*TIMESTEP_SIZE); public static final double TRADE_ADJUSTMENT_COST_RATE = getDoubleProperty("TRADE_ADJUSTMENT_COST_RATE", 0.006); // Cost of changing imports/exports - + public static final boolean ALLOW_NEGATIVE_STOCKS = getBooleanProperty("ALLOW_NEGATIVE_STOCKS", true); // Warning: if set to false then countries might not meet demand + // Price caps public static final String PRICE_CAP_FILE = DATA_DIR + File.separator + "price_caps.csv"; @@ -582,7 +583,7 @@ public class ModelConfig { public static final double INIT_WOOD_STOCK = getDoubleProperty("INIT_WOOD_STOCK", 1200.0); // million m3 public static final double WOOD_BIOMASS_CONVERSION_FACTOR = getDoubleProperty("WOOD_BIOMASS_CONVERSION_FACTOR", 0.3); // m3 to tC-eq p.16 [https://doi.org/10.5194/gmd-13-5425-2020] public static final double FOREST_MANAGEMENT_COST = IS_FORESTRY_ON ? getDoubleProperty("FOREST_MANAGEMENT_COST", 2.0) : 0.0; // establishment, management etc. $1000/ha - public static final double FOREST_BASE_COST = getDoubleProperty("FOREST_BASE_COST", 0.12); // $1000/ha + public static final double FOREST_BASE_COST = getDoubleProperty("FOREST_BASE_COST", 0.12); // cost independent of management intensity $1000/ha public static final double WOOD_TRADE_BARRIER = getDoubleProperty("WOOD_TRADE_BARRIER", 0.1); //$1000/m3 public static final double INIT_ROUNDWOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_ROUNDWOOD_PRICE", 0.0655) : 0.0; // $1000/m3 public static final double INIT_FUELWOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_FUELWOOD_PRICE", 0.0461) : 0.0; // $1000/m3 @@ -593,12 +594,14 @@ public class ModelConfig { public static final String CARBON_DEMAND_FILENAME = getProperty("CARBON_DEMAND_FILENAME", "carbon_demand.csv"); public static final String CARBON_DEMAND_FILE = getProperty("CARBON_DEMAND_FILE", DATA_DIR + File.separator + CARBON_DEMAND_FILENAME); public static final int CARBON_HORIZON = getIntProperty("CARBON_HORIZON", 30); // time period for calculating carbon credits - public static final double INIT_CARBON_PRICE = IS_CARBON_ON ? getDoubleProperty("INIT_CARBON_PRICE", 0.02) : 0.0; // $1000/tC-eq + public static final double INIT_CARBON_PRICE = IS_CARBON_ON ? getDoubleProperty("INIT_CARBON_PRICE", 0.03) : 0.0; // $1000/tC-eq public static final double INIT_CARBON_STOCK = getDoubleProperty("INIT_CARBON_STOCK", 100.0); // MtC-eq public static final String CARBON_OPTIONS_FILENAME = getProperty("CARBON_OPTIONS_FILENAME", "carbon_options.csv"); // config for what is included in carbon market public static final String CARBON_OPTIONS_FILE = getProperty("CARBON_OPTIONS_FILE", DATA_DIR + File.separator + CARBON_OPTIONS_FILENAME); public static final double CARBON_FOREST_MAX_PROPORTION = getDoubleProperty("CARBON_FOREST_MAX_PROPORTION", 1.0); // maximum proportion of land cover as carbon forest - public static final double CARBON_FOREST_CONVERSION_PENALTY = getDoubleProperty("CARBON_FOREST_CONVERSION_PENALTY", 10.0); // Penality for removing carbon forest 1000$/ha - public static final double CARBON_TRADE_ADJ_COST_MULTIPLIER = getDoubleProperty("CARBON_TRADE_ADJ_COST_MULTIPLIER", 0.1); // No physical goods traded so can have lower cost + public static final double CARBON_FOREST_CONVERSION_PENALTY = getDoubleProperty("CARBON_FOREST_CONVERSION_PENALTY", 10.0); // Penalty for removing carbon forest 1000$/ha + public static final double CARBON_TRADE_ADJ_COST_MULTIPLIER = getDoubleProperty("CARBON_TRADE_ADJ_COST_MULTIPLIER", 0.3); // No physical goods traded so can have lower cost + // How demand is allocated. Options: global (countries can export carbon credits to market), country (global demand allocated by GDP), price (no demand, exogenous price) + public static final String CARBON_DEMAND_METHOD = getProperty("CARBON_DEMAND_METHOD", "global"); } diff --git a/src/ac/ed/lurg/carbon/CarbonFluxItem.java b/src/ac/ed/lurg/carbon/CarbonFluxItem.java index a6f2210eef756ccbc94969425f04f1961912c038..e1183a0497b59a0faba07c6fa579760d9b69bf82 100644 --- a/src/ac/ed/lurg/carbon/CarbonFluxItem.java +++ b/src/ac/ed/lurg/carbon/CarbonFluxItem.java @@ -29,10 +29,7 @@ public class CarbonFluxItem implements RasterItem, Serializable { for (LandCoverType otherLc : LandCoverType.getConvertibleTypes()) { if (otherLc.equals(lcType)) continue; - // Converting to this type so add to carbon credit rate carbonCreditRate.merge(new LccKey(otherLc, lcType), totalFlux, Double::sum); - // Converting from this type so subtract from carbon credit rate - //carbonCreditRate.merge(new LccKey(lcType, otherLc), -totalFlux, Double::sum); } } diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java index 2b6b2e86c95aa98a1c7adc1bfd15f4aa4711e85c..f4a891580840f32ba9d58532989f676d81176b11 100644 --- a/src/ac/ed/lurg/country/CountryAgent.java +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -216,7 +216,7 @@ public class CountryAgent extends AbstractCountryAgent { double changeUp, changeDown; if (Math.abs(baseTrade) < 1e-3) { // to set initial trade when starting from zero - changeUp = changeDown = getCurrentCarbonDemand(); + changeUp = changeDown = ModelConfig.CARBON_DEMAND_METHOD.equals("country") ? getCurrentCarbonDemand() : 1; } else { double maxOfProdOrSupply = carbonUsageData.getCarbonCredits() + Math.max(baseTrade, 0); changeUp = globalCarbonPrice.getTradeChangeUp(maxOfProdOrSupply); diff --git a/src/ac/ed/lurg/country/GlobalPrice.java b/src/ac/ed/lurg/country/GlobalPrice.java index 552b10b77d0878d4df9d00c48280e2e51b816fe0..6780dfd3ceed26e9a2b230d8ca2b04b4de060cd7 100644 --- a/src/ac/ed/lurg/country/GlobalPrice.java +++ b/src/ac/ed/lurg/country/GlobalPrice.java @@ -110,7 +110,7 @@ public class GlobalPrice implements Serializable { // Market lambda lower when the rate of change in stock is decreasing // Acts as a damping mechanism to reduce price fluctuation double lambda = Math.atan2(Math.abs(stockChange), Math.abs(prevStockChange)) / (0.5 * Math.PI) * marketLambda; - LogWriter.println("lambda="+lambda); + LogWriter.println(" lambda="+lambda); // Prices increase as a proportion of stockLevel and decrease as a proportion of production double denominator = stockChange < 0 ? stockLevel : production; @@ -163,6 +163,14 @@ public class GlobalPrice implements Serializable { } } + public GlobalPrice createWithSetPrice(double newPrice, double newImports, double newExportAmountBeforeLoss, Timestep thisTimeStep, + double production) { + LogWriter.println(String.format(" imports %.2f, exports %.2f", newImports, newExportAmountBeforeLoss), 2); + + return new GlobalPrice(thisTimeStep, newPrice, stockLevel, newImports, newExportAmountBeforeLoss, + 0, exportPrice, 0, 0, production); + } + @Override public String toString() { return "GlobalPrice [timestep=" + timestep.getTimestep() + ", exportPrice=" + exportPrice + ", importAmount=" + importAmount @@ -197,7 +205,7 @@ public class GlobalPrice implements Serializable { } public double getTradeChangeUp(double maxOfProdOrSupply) { - if (ModelConfig.DEBUG_LIMIT_COUNTRIES) { + if (ModelConfig.ALLOW_NEGATIVE_STOCKS | ModelConfig.DEBUG_LIMIT_COUNTRIES) { return maxOfProdOrSupply * ModelConfig.MAX_IMPORT_CHANGE; } // plus 5% of global production divided by number of countries in case country has 0 supply diff --git a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java index 2be319e74ef20251fb21baceabc34b391a444901..110aeab507bd354494036b4557fb8df431cb2162 100644 --- a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java +++ b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java @@ -113,8 +113,9 @@ public abstract class AbstractSSPDemandManager extends AbstractDemandManager { protected double getCarbonDemand(SingleCountry country, int year) { - if (!ModelConfig.IS_CARBON_ON | ModelConfig.IS_CALIBRATION_RUN) + if (!ModelConfig.IS_CARBON_ON | ModelConfig.IS_CALIBRATION_RUN | !ModelConfig.CARBON_DEMAND_METHOD.equals("country")) { return 0; + } SspData sd = sspManager.get(ssp_scenario, year, country); if (sd == null) { diff --git a/src/ac/ed/lurg/demand/CarbonDemandManager.java b/src/ac/ed/lurg/demand/CarbonDemandManager.java index 6d8f44eff267de6fb99daef32e02862eb54c8079..8101855085739ab6617f2ee25dcf4f7f40646b46 100644 --- a/src/ac/ed/lurg/demand/CarbonDemandManager.java +++ b/src/ac/ed/lurg/demand/CarbonDemandManager.java @@ -4,61 +4,44 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import ac.ed.lurg.ModelConfig; import ac.ed.lurg.utils.Interpolator; import ac.ed.lurg.utils.LogWriter; +import ac.ed.lurg.utils.StringTabularReader; + +import javax.swing.plaf.basic.BasicInternalFrameUI; public class CarbonDemandManager { - private Map<Integer, Double> globalCarbonDemand; // global demand for carbon sequestration - private static final int YEAR_COL = 0; - private static final int DEMAND_COL = 1; + private final Map<Integer, Double> globalCarbonDemand = new HashMap<>(); // global demand for carbon offsets + private final Map<Integer, Double> globalCarbonPrice = new HashMap<>(); // $1000/t public CarbonDemandManager() { readCarbonDemandData(); } public void readCarbonDemandData() { - - Map<Integer, Double> data = new HashMap<Integer, Double>(); - String filename = ModelConfig.CARBON_DEMAND_FILE; - try { - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - Integer year; - Double demand; - reader.readLine(); // read header - - while ((line=reader.readLine()) != null) { - String[] tokens = line.split(","); - - if (tokens.length < 2) - LogWriter.printlnError("Too few columns in " + filename + ", " + line); - - year = Integer.valueOf(tokens[YEAR_COL]); - demand = Double.valueOf(tokens[DEMAND_COL]); - - data.put(year, demand); - } - reader.close(); - - } catch (IOException e) { - LogWriter.printlnError("Failed in reading carbon demand data"); - LogWriter.print(e); + StringTabularReader reader = new StringTabularReader(",", + new String[] {"Year", "Demand_Mt", "Price"}); + reader.read(filename); + List<Map<String, String>> rowList = reader.getRowList(); + for (Map<String, String> row : rowList) { + int year = Integer.parseInt(row.get("Year")); + double demand = Double.parseDouble(row.get("Demand_Mt")); + double price = Double.parseDouble(row.get("Price")); + globalCarbonDemand.put(year, demand); + globalCarbonPrice.put(year, price); } - globalCarbonDemand = data; - LogWriter.println("Processed " + filename, 2); } public double getGlobalCarbonDemand(int year) { - int downYear = (year/5) * 5; - int upYear = downYear + 5; - Double lowerD = globalCarbonDemand.get(downYear); - Double upperD = globalCarbonDemand.get(upYear); - double factor = ((double)(year - downYear)) / (upYear - downYear); - Double d = Interpolator.interpolate(lowerD, upperD, factor); - return d; + return globalCarbonDemand.getOrDefault(year, Double.NaN); + } + + public double getGlobalCarbonPrice(int year) { + return globalCarbonPrice.getOrDefault(year, Double.NaN); } }