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);
 	}
 }