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() {