diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java
index 86350e1f3732cd35df42a619771cbfeccfb6e1df..606f6b8e2936261c8f60bca0e12ab013c25fece2 100644
--- a/src/ac/ed/lurg/InternationalMarket.java
+++ b/src/ac/ed/lurg/InternationalMarket.java
@@ -45,8 +45,7 @@ public class InternationalMarket {
 			}
 			
 			carbonPrice = GlobalPrice.createInitial(ModelConfig.INIT_CARBON_PRICE, 0);
-			woodPrice = GlobalPrice.createInitial(ModelConfig.INIT_WOOD_PRICE, ModelConfig.INIT_WOOD_STOCK);
-
+			woodPrice = GlobalPrice.createInitial(ModelConfig.INIT_WOOD_PRICE, ModelConfig.INIT_WOOD_STOCK);		
 		}
 		else {
 			List<Object> deserializedPrices = deserializeGlobalPrice();
@@ -54,8 +53,14 @@ public class InternationalMarket {
 			//carbonPrice = (GlobalPrice) deserializedPrices.get(1);
 			carbonPrice = GlobalPrice.createInitial(ModelConfig.INIT_CARBON_PRICE, 0);
 			woodPrice = (GlobalPrice) deserializedPrices.get(2);
-
+						
+			if (ModelConfig.RESET_ENERGYCROP_PRICE) {
+				double previousExportPrice = worldPrices.get(CropType.ENERGY_CROPS).getExportPrice();
+				GlobalPrice newPrice = worldPrices.get(CropType.ENERGY_CROPS).createPriceAdjustedByFactor(ModelConfig.INITAL_PRICE_ENERGYCROPS / previousExportPrice);
+				worldPrices.put(CropType.ENERGY_CROPS, newPrice);
+			}
 		}
+		
 		priceShockManager = new PriceShockManager();
 	}
 
@@ -83,6 +88,17 @@ public class InternationalMarket {
 		Map<CropType, Double> initialStockLevels = new StockReader().read();
 		return initialStockLevels;
 	}
+	
+	void resetStocks() {
+		Map<CropType, Double> initialStockLevels = getInitialStockLevels();
+		for (Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) {
+			Double initialStock = initialStockLevels.get(entry.getKey());
+			if (initialStock != null)
+				entry.getValue().resetStock(initialStock);
+		}
+		woodPrice.resetStock(ModelConfig.INIT_WOOD_STOCK);
+		carbonPrice.resetStock(0.0);
+	}
 
 	void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double carbonDemand, Timestep timestep) {
 		CropToDoubleMap totalImportCommodities = new CropToDoubleMap();
@@ -123,6 +139,7 @@ public class InternationalMarket {
 		
 		// Update carbon price
 		double totalCarbonSequestered = 0;
+		@SuppressWarnings("unused")
 		double totalCarbonEmitted = 0;
 		for (AbstractCountryAgent ca : countryAgents) {
 			double netCarbonFlux = ca.getNetCarbonFlux();
diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
index 8e274af0c6d41b3876a2f90a98af6e79e0585a75..8cb8be40f6dead5bf35ed696064351c5d126d7d4 100755
--- a/src/ac/ed/lurg/ModelConfig.java
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -13,13 +13,13 @@ import ac.ed.lurg.types.PriceType;
 import ac.ed.lurg.utils.LogWriter;
 
 public class ModelConfig {
-	
+
 	private Properties configFile;
 	private static ModelConfig modelConfig;
 	public static final String CONFIG_FILE = System.getProperty("CONFIG_FILE");
-	
+
 	private static parameterShocksReader shocksReader;
-	
+
 	private ModelConfig() {
 		configFile = new Properties();
 		try {
@@ -46,14 +46,14 @@ public class ModelConfig {
 
 		return sb.toString();
 	}
-	
+
 	private static ModelConfig getModelConfig() {
 		if (modelConfig == null)
 			modelConfig = new ModelConfig();
 
 		return modelConfig;
 	}
-	
+
 	private static String getProperty(String prop) {
 		return getModelConfig().getProp(prop);
 	}
@@ -64,7 +64,7 @@ public class ModelConfig {
 	private String getProp(String prop) {
 		return configFile.getProperty(prop);
 	}
-	
+
 	private static Integer getIntProperty(String prop, Integer defaultInt) {
 		Integer propValue = getModelConfig().getIntProp(prop);
 		return propValue == null ? defaultInt : propValue;
@@ -73,7 +73,7 @@ public class ModelConfig {
 		String v = configFile.getProperty(prop);
 		return v==null ? null : Integer.valueOf(v);
 	}
-	
+
 	@SuppressWarnings("unused")
 	private static Long getLongProperty(String prop, Long defaultLong) {
 		Long propValue = getModelConfig().getLongProp(prop);
@@ -83,7 +83,7 @@ public class ModelConfig {
 		String v = configFile.getProperty(prop);
 		return v==null ? null : Long.valueOf(v);
 	}
-	
+
 	private static Double getDoubleProperty(String prop, Double defaultDouble) {
 		Double propValue = getModelConfig().getDoubleProp(prop);
 		return propValue == null ? defaultDouble : propValue;
@@ -92,15 +92,15 @@ public class ModelConfig {
 		String v = configFile.getProperty(prop);
 		return v==null ? null :  Double.valueOf(v);
 	}
-	
+
 	private static Boolean getBooleanProperty(String prop, Boolean defaultBoolean) {
 		return getModelConfig().getBooleanProp(prop, defaultBoolean);
 	}
 	private boolean getBooleanProp(String prop, Boolean defaultBoolean) {
 		String v = configFile.getProperty(prop);
 		return v==null ? defaultBoolean :  Boolean.valueOf(v);
-	}
-	
+	}	
+
 	public static double updateParameterForShocks(int year, String parameter) {
 
 		Double value = null;
@@ -127,7 +127,6 @@ public class ModelConfig {
 		return value;
 	}
 
-		
 	public static final boolean SUPPRESS_STD_OUTPUT = getBooleanProperty("SUPPRESS_STD_OUTPUT", Boolean.FALSE);
 
 	// Directory information
@@ -364,19 +363,7 @@ public class ModelConfig {
 	public static final String BIOENERGY_DEMAND_SCENARIO = getProperty("BIOENERGY_DEMAND_SCENARIO", "SSP2_RCP45");
 	public static final String GEN2_BIOENERGY_MODEL = getProperty("GEN2_BIOENERGY_MODEL", "ENSEMBLE-MEAN");
 	public static final double BIOENERGY_DEMAND_SHIFT = IS_CALIBRATION_RUN ? 1.0 : getDoubleProperty("BIOENERGY_DEMAND_SHIFT", 1.0);
-//	public static final double BIOENERGY_HEATING_VALUE_GJ_PER_T = getDoubleProperty("BIOENERGY_HEATING_VALUE_GJ_PER_T", 17.5); // GJ per t DM
-	
-	//Dietary stuff
-	public static final double RUMINANT_CHANGE_ANNUAL_RATE  = getDoubleProperty("RUMINANT_CHANGE_ANNUAL_RATE", 0.0);
-	public static final double MONOGASTRIC_CHANGE_ANNUAL_RATE  = getDoubleProperty("MONOGASTRIC_CHANGE_ANNUAL_RATE", 0.00);
-	public static final boolean CONSTANT_DIET_LOW_INCOME = getBooleanProperty("CONSTANT_DIET_LOW_INCOME", false);
-	public static final boolean CONSTANT_DIET_HIGH_INCOME  = getBooleanProperty("CONSTANT_DIET_HIGH_INCOME", false);
-	//below should all sum to zero
-	public static final double CEREALS_SUB_PROPORTION  = getDoubleProperty("CEREALS_SUB_PROPORTION", 0.3);
-	public static final double OILCROPSPULSES_SUB_PROPORTION  = getDoubleProperty("OILCROPSPULSES_SUB_PROPORTION", 0.25);
-	public static final double STARCHY_ROOTS_SUB_PROPORTION  = getDoubleProperty("STARCHY_ROOTS_SUB_PROPORTION", 0.25);
-	public static final double FRUITVEG_SUB_PROPORTION  = getDoubleProperty("FRUITVEG_SUB_PROPORTION", 0.1);
-	public static final double SUGAR_SUB_PROPORTION  = getDoubleProperty("SUGAR_SUB_PROPORTION", 0.1);
+	//	public static final double BIOENERGY_HEATING_VALUE_GJ_PER_T = getDoubleProperty("BIOENERGY_HEATING_VALUE_GJ_PER_T", 17.5); // GJ per t DM
 
 	public static final double MARKET_LAMBA = getDoubleProperty("MARKET_LAMBA", 0.4); // controls international market price adjustment rate
 	public static final boolean PRICE_UPDATE_BY_MARKET_IMBALANCE = getBooleanProperty("PRICE_UPDATE_BY_MARKET_IMBALANCE", false);;
@@ -395,7 +382,7 @@ public class ModelConfig {
 	// So we do not represent crops covering (1341.911-1239.03)/1547.464=6.65% of cropland.  Additionally 10.3% of cropland is set aside, fallow or failed crops, 
 	public static final double UNHANDLED_CROP_RATE = getDoubleProperty("UNHANDLED_CROP_RATE", 0.0665);  // mostly forage crops
 	public static final double SETASIDE_RATE = getDoubleProperty("SETASIDE_RATE", 0.103);  // includes aside, fallow and failed cropland areas 
-	
+
 	public static final double OTHER_INTENSITY_COST = getDoubleProperty("OTHER_INTENSITY_COST", 0.8);
 	public static final double OTHER_INTENSITY_PARAM = getDoubleProperty("OTHER_INTENSITY_PARAM", 3.22);
 
@@ -403,7 +390,7 @@ public class ModelConfig {
 	public static final double IRRIG_COST_MULTIPLIER = getDoubleProperty("IRRIG_COST_MULTIPLIER", 1.0);
 	public static final double FERTILISER_COST_PER_T = getDoubleProperty("FERTILISER_COST_PER_T", 1.4); // $500/t, 18% N/t
 	public static final double FERTILISER_MAX_COST = FERTILISER_COST_PER_T * MAX_FERT_AMOUNT/1000;
-	
+
 	public static final double DOMESTIC_PRICE_MARKUP = getDoubleProperty("DOMESTIC_PRICE_MARKUP", 1.0);
 	public static final double TRANSPORT_LOSSES = getDoubleProperty("TRANSPORT_LOSSES", 0.05);  // in international trade
 	public static final double TRANSPORT_COST = getDoubleProperty("TRANSPORT_COST", 0.1);  // 10% transport cost
@@ -413,14 +400,14 @@ public class ModelConfig {
 	public static final double TRADE_BARRIER_ENERGY_CROPS = getDoubleProperty("TRADE_BARRIER_ENERGY_CROPS", 0.01);  // price factor in international trade, transport cost and real trade barriers
 	public static final boolean SHOCKS_POSSIBLE = getBooleanProperty("SHOCKS_POSSIBLE", false);
 	public static final double YIELD_SHOCK_MAGNIFIER = getDoubleProperty("YIELD_SHOCK_MAGNIFIER", 1.0);
-	
+
 	public static final boolean PROTECTED_AREAS_ENABLED = getBooleanProperty("PROTECTED_AREAS_ENABLED", true);
 	public static final double MIN_NATURAL_RATE = getDoubleProperty("MIN_NATURAL_RATE", 0.05);
 	public static final double MAX_CHINA_LAND_EXPANSION_RATE = getDoubleProperty("MAX_CHINA_LAND_EXPANSION_RATE", 0.011*0.4); // 1.1% max forest change 40% of natural land is forest
 
 	public static final boolean DEBUG_JUST_DEMAND_OUTPUT = getBooleanProperty("DEBUG_JUST_DEMAND_OUTPUT", false);
 	public static final boolean DEBUG_LIMIT_COUNTRIES = getBooleanProperty("DEBUG_LIMIT_COUNTRIES", false);
-	public static final String DEBUG_COUNTRY_NAME = getProperty("DEBUG_COUNTRY_NAME", "United States of America");
+	public static final String DEBUG_COUNTRY_NAME = getProperty("DEBUG_COUNTRY_NAME", "China");
 	public static final String GAMS_COUNTRY_TO_SAVE = getProperty("GAMS_COUNTRY_TO_SAVE", "China");;
 	public static final boolean EXCLUDE_COUNTRIES_IN_LIST = getBooleanProperty("EXCLUDE_COUNTRIES_IN_LIST", false);
 	public static final String EXCLUDED_COUNTRIES_FILE = DATA_DIR + File.separator + "countries_excluded.csv";
@@ -453,6 +440,8 @@ public class ModelConfig {
 	public static final boolean APPLY_EXPORT_TAXES = getBooleanProperty("APPLY_EXPORT_TAXES", false);
 	public static final double EXPORT_TAX_RATE = getDoubleProperty("EXPORT_TAX_RATE", 1.0);
 	public static final double EXPORT_TAX_THRESHOLD = getDoubleProperty("EXPORT_TAX_THRESHOLD", 0.1);
+	public static final int RESET_STOCK_YEAR = IS_CALIBRATION_RUN ? 0 : getIntProperty("RESET_STOCK_YEAR", 2010);
+	public static final String MIN_MAX_TRADE_FILE = getProperty("MIN_MAX_TRADE_FILE", OUTPUT_DIR + File.separator + "minMaxNetImport.csv");
 	
 	// Forestry and carbon market parameters
 	public static final String CONVERSION_COST_FILE = DATA_DIR + File.separator + "conversion_costs.csv"; // cost of converting from one land cover to another, $1000/ha
@@ -461,7 +450,7 @@ public class ModelConfig {
 	public static final String TIMBER_DEMAND_FILENAME = getProperty("TIMBER_DEMAND_FILENAME", "timber_demand.csv");
 	public static final String TIMBER_DEMAND_FILE = getProperty("TIMBER_DEMAND_FILE", DATA_DIR + File.separator + TIMBER_DEMAND_FILENAME);
 
-	public static final double INIT_WOOD_STOCK = getDoubleProperty("INIT_WOOD_STOCK", 1000.0); // tC-eq
+	public static final double INIT_WOOD_STOCK = getDoubleProperty("INIT_WOOD_STOCK", 500.0); // tC-eq
 	public static final double IND_ROUNDWOOD_DEMAND_ELASTICITY = getDoubleProperty("IND_ROUNDWOOD_DEMAND_ELASTICITY",  0.3123881);
 	public static final double FUELWOOD_DEMAND_ELASTICITY = getDoubleProperty("FUELDWOOD_DEMAND_ELASTICITY", -0.3598551);
 	public static final String WOOD_NET_IMPORTS_FILENAME = getProperty("WOOD_NET_IMPORTS_FILENAME", "wood_net_imports.csv");
@@ -476,5 +465,7 @@ public class ModelConfig {
 	public static final int CARBON_HORIZON = getIntProperty("CARBON_HORIZON", 30);
 	public static final double WOOD_TRADE_BARRIER = getDoubleProperty("WOOD_TRADE_BARRIER", 0.05); //$1000/tC
 	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_WOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_WOOD_PRICE", 0.3) : 0.0; // $1000/tC-eq
+	public static final double INIT_WOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_WOOD_PRICE", 0.4) : 0.0; // $1000/tC-eq
+	public static final boolean RESET_ENERGYCROP_PRICE = getBooleanProperty("RESET_ENERGYCROP_PRICE", true);
+	public static final boolean ENABLE_VEGETATION_CLEARANCE_COST = getBooleanProperty("ENABLE_VEGETATION_CLEARANCE_COST", true);
 }
diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java
index 3a2f4a15a1de2b052a207d53b6f3e04f253017ea..3ff119d4bf124eb5cfd0a80b2b8b293c58bbd31d 100644
--- a/src/ac/ed/lurg/ModelMain.java
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -138,7 +138,6 @@ public class ModelMain {
 	}
 	
 	private void doTimestep(Timestep timestep) {
-		System.gc();
 		LogWriter.println("Timestep: " + timestep.toString());
 		LogWriter.println("Memory usage 1: " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024.0*1024.0*1024.0));
 
@@ -171,6 +170,10 @@ public class ModelMain {
 		
 		countryAgents.determineProductionForAll(timestep, yieldSurfaces, currentIrrigationData, carbonFluxData, woodYieldData,
 				conversionCosts, carbonDemandIncrease);
+		
+		if (ModelConfig.RESET_STOCK_YEAR == timestep.getYear())
+			internationalMarket.resetStocks();
+		
 		internationalMarket.determineInternationalTrade(countryAgents.getAll(), carbonDemand, timestep);
 		
 		int i = 0;		
@@ -437,17 +440,18 @@ public class ModelMain {
 	private void writeCountryDemandFile(Timestep timestep){
 
 		try {
-			StringBuffer sbHeadings = new StringBuffer("Year, Country, Commodity, Demand, BioenergyDemand");
+			StringBuffer sbHeadings = new StringBuffer("Year, Country, Commodity, Demand, BioenergyDemand, ConsumerPrice");
 			BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.COUNTRY_DEMAND_FILE, sbHeadings.toString());
 
 			for (AbstractCountryAgent country : countryAgents.getAll()) {
 				for (CommodityType commodity : CommodityType.getAllFoodItems()) {
 					double bioenergyDemand = demandManager.getFirstGenBioenergyDemand(country.getCountry(), timestep.getYear(), commodity);
 					double  demand =  country.getCurrentProjectedDemand().get(commodity);
+					double consumerPrice = country.getCurrentConsumerPrice(commodity);
 
 					StringBuffer sbData = new StringBuffer();
 					sbData.append(String.format("%d,%s,%s", timestep.getYear(), country.getCountry(), commodity.getGamsName()));
-					sbData.append(String.format(",%.3f,%.3f", demand, bioenergyDemand));
+					sbData.append(String.format(",%.4f,%.4f,%.4f", demand, bioenergyDemand, consumerPrice));
 
 					outputFile.write(sbData.toString());
 					outputFile.newLine();
@@ -664,8 +668,9 @@ public class ModelMain {
 			rasterToSerialise.put(entry.getKey(), newLuItem);
 		}
 		try {
-			LogWriter.println("Starting serializing LandUse to " + ModelConfig.SERIALIZED_LAND_USE_FILE);
-			FileOutputStream fileOut = new FileOutputStream(ModelConfig.SERIALIZED_LAND_USE_FILE);
+			String fileStr = ModelConfig.IS_CALIBRATION_RUN ? ModelConfig.SERIALIZED_LAND_USE_FILE : ModelConfig.CHECKPOINT_LAND_USE_FILE;
+			LogWriter.println("Starting serializing LandUse to " + fileStr);
+			FileOutputStream fileOut = new FileOutputStream(fileStr);
 			ObjectOutputStream out = new ObjectOutputStream(fileOut);
 			out.writeObject(rasterToSerialise);
 			out.close();
@@ -781,7 +786,7 @@ public class ModelMain {
 	
 	/** Get wood yield data */
 	private WoodYieldRasterSet getWoodYieldData(Timestep timestep) {
-		if (ModelConfig.IS_FORESTRY_ON) {
+		if (ModelConfig.IS_FORESTRY_ON || ModelConfig.ENABLE_VEGETATION_CLEARANCE_COST) {
 			return woodYieldReader.getWoodYields(globalLandUseRaster, timestep, internationalMarket.getWoodPrice().getExportPrice());	
 		} else {
 			return new WoodYieldRasterSet(desiredProjection);
diff --git a/src/ac/ed/lurg/carbon/AgeFunction.java b/src/ac/ed/lurg/carbon/AgeFunction.java
deleted file mode 100644
index 461cab6e9dfc449e4e1f38c3b0000ea283c3df24..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/carbon/AgeFunction.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package ac.ed.lurg.carbon;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-import ac.ed.lurg.utils.BilinearInterpolator;
-
-public class AgeFunction implements Serializable {
-	private static final long serialVersionUID = -6068915999792275002L;
-	public static final int MAX_AGE = 150;
-	public static final int YEAR_INTERVAL = 20;
-	private static final int YEAR_OFFSET = 10;
-	public static final int AGE_INTERVAL = 10;
-	
-	private Map<Integer, Map<Integer, Double>> data = new HashMap<Integer, Map<Integer, Double>>();
-	
-	public void setData(int year, int age, double yield) {
-		Map<Integer, Double> yieldMap = data.computeIfAbsent(year, o -> new HashMap<Integer, Double>());
-		yieldMap.put(age, yield);
-	}
-	
-	public double getValue(int year, int age) {
-		int yearLower = Math.floorDiv(year, YEAR_INTERVAL) * YEAR_INTERVAL + YEAR_OFFSET;
-		int yearUpper = yearLower + YEAR_INTERVAL;
-		int ageLower = Math.floorDiv(age, AGE_INTERVAL) * AGE_INTERVAL;
-		ageLower = Math.min(ageLower, MAX_AGE);
-		int ageUpper = ageLower + AGE_INTERVAL;
-		ageUpper = Math.min(ageUpper, MAX_AGE);
-		double q11 = data.get(yearLower).get(ageLower);
-		double q12 = data.get(yearLower).get(ageUpper);
-		double q21 = data.get(yearUpper).get(ageLower);
-		double q22 = data.get(yearUpper).get(ageUpper);
-		// Edge case when age > MAX_AGE. Use linear interpolation.
-		if (ageLower == ageUpper) {
-			return q11 + (year - yearLower) * ((q21 - q11) / (yearUpper - yearLower));
-		}
-		double d = BilinearInterpolator.interpolate(year, age, yearLower, yearUpper, ageLower, ageUpper, q11, q12, q21, q22);
-		return d;
-		
-	}
-
-	public int getMaxAge() {
-		return MAX_AGE;
-	}
-	
-}
diff --git a/src/ac/ed/lurg/country/AbstractCountryAgent.java b/src/ac/ed/lurg/country/AbstractCountryAgent.java
index f6cc92d43ab593df641048471cb86791e8809970..16680218da0235a8b26e14c1d762501c4fa0b31c 100644
--- a/src/ac/ed/lurg/country/AbstractCountryAgent.java
+++ b/src/ac/ed/lurg/country/AbstractCountryAgent.java
@@ -22,6 +22,7 @@ public abstract class AbstractCountryAgent {
 	private Map<CropType, GlobalPrice> currentWorldPrices;
 	protected Map<CropType, CountryPrice> currentCountryPrices;
 	private Map<CropType, CountryPrice> previousCountryPrices;
+	private Map<CommodityType, Double> currentConsumerCommodityPrices;
 	protected CountryPrice currentCarbonPrice;
 	protected CountryPrice currentTimberPrice;
 	protected Timestep currentTimestep;
@@ -102,18 +103,27 @@ public abstract class AbstractCountryAgent {
 		if (!ModelConfig.PRICE_ELASTIC_DEMAND)
 			return null;
 						
-		Map<CommodityType, Double> prices = new HashMap<CommodityType, Double>();
+		currentConsumerCommodityPrices = new HashMap<CommodityType, Double>();
 		
 		for (CommodityType commodity : CommodityType.getAllFoodItems()) {
-			double commPricePlum = getCommPriceFromCropPrice(commodity);
-			prices.put(commodity, commPricePlum);
+			double commPricePlum = 0;
+			Map<CropType, Double> demandFract = currentDemandFract.get(commodity);
+			
+			for (CropType crop : commodity.getCropTypes()) {
+				double consumerCropPrice = currentCountryPrices.get(crop).getConsumerPrice();
+				commPricePlum += consumerCropPrice * demandFract.get(crop);  // weight price by base demand of each cereal crop
+			}
+
+			currentConsumerCommodityPrices.put(commodity, commPricePlum);
 			LogWriter.println("Producer price for " + commodity.getGamsName() + " is " + commPricePlum);
 		}
 		
-		return prices;
+		return currentConsumerCommodityPrices;
 	}
 	
-	protected abstract double getCommPriceFromCropPrice(CommodityType commodity);
+	public double getCurrentConsumerPrice(CommodityType commodity) {
+		return currentConsumerCommodityPrices.get(commodity);
+	}
 
 	public Map<CommodityType, Double> getCurrentProjectedDemand() {
 		return currentProjectedDemand;
diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
index 0f17e1b2ea84499374772535758457001a3e79cd..7a1b60aa56cc44aa9cb81c55a82b4bfa469b923b 100644
--- a/src/ac/ed/lurg/country/CountryAgent.java
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -21,6 +21,8 @@ import ac.ed.lurg.forestry.WoodYieldItem;
 import ac.ed.lurg.landuse.CropUsageData;
 import ac.ed.lurg.landuse.IrrigationItem;
 import ac.ed.lurg.landuse.LandUseItem;
+import ac.ed.lurg.shock.MinMaxNetImportManager;
+import ac.ed.lurg.shock.MinMaxNetImportManager.LimitType;
 import ac.ed.lurg.landuse.LccKey;
 import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.types.CommodityType;
@@ -43,15 +45,20 @@ public class CountryAgent extends AbstractCountryAgent {
 	private GamsRasterOutput previousGamsRasterOutput;
 	private RasterSet<IntegerRasterItem> yieldClusters;
 	private Map<CropType, Double> subsidyRates;
+	private Map<Integer, Map<CropType, Double>> exportRestrictions;
+	private Map<Integer, Map<LimitType, Map<CropType, Double>>> minMaxTradeLimits;
 	private boolean saveGamsGdxFiles;
 
 	public CountryAgent(AbstractDemandManager demandManager,CompositeCountry country, RasterSet<LandUseItem> cropAreaRaster,
 			Map<CropType, CropUsageData> cropUsageData, Map<CropType, Double> tradeBarriers, RasterSet<IntegerRasterItem> yieldClusters,
-			Map<CropType, Double> subsidyRates, Map<WoodType, WoodUsageData> countryWoodData) {
+			Map<CropType, Double> subsidyRates, Map<WoodType, WoodUsageData> countryWoodData,
+			Map<Integer, Map<CropType, Double>> exportRestrictions, Map<Integer, Map<LimitType, Map<CropType, Double>>> minMaxTradeLimits) {
 
 		super(demandManager, country, tradeBarriers);
 		this.yieldClusters = yieldClusters;
 		this.subsidyRates = subsidyRates;
+		this.exportRestrictions = exportRestrictions;
+		this.minMaxTradeLimits = minMaxTradeLimits;
 		
 		GamsRasterOutput initialData = new GamsRasterOutput(cropAreaRaster, cropUsageData, countryWoodData);
 		previousGamsRasterOutput = initialData;
@@ -184,7 +191,7 @@ public class CountryAgent extends AbstractCountryAgent {
 			double carbonDemandIncrease) {
 		double allowedImportChange;
 
-		if (currentTimestep.isInitialTimestep() || (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION)) {  // initialisation time-step
+		if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION) {  // initialisation time-step
 			allowedImportChange = 0.0;
 		}
 		else { // normal (not the initial) time-step
@@ -192,6 +199,7 @@ public class CountryAgent extends AbstractCountryAgent {
 		}
 
 		Map<CropType, TradeConstraint> importConstraints = new HashMap<CropType, TradeConstraint>();
+		Map<CropType, Double> currentExportRestictions = (exportRestrictions == null) ? null : exportRestrictions.get(currentTimestep.getYear());
 
 		for (Map.Entry<CropType, CropUsageData> entry : previousGamsRasterOutput.getCropUsageData().entrySet()) {
 			CropUsageData cropUsage = entry.getValue();
@@ -212,8 +220,22 @@ public class CountryAgent extends AbstractCountryAgent {
 			if (CropType.ENERGY_CROPS.equals(crop) && baseTrade == 0) { // could apply this logic for all crops?
 				changeDown = changeUp = allowedImportChange * currentGen2EcDemand;
 			}
-
-			importConstraints.put(crop, new TradeConstraint(baseTrade - changeDown, baseTrade + changeUp));
+			
+			double restiction = 0;
+			if (currentExportRestictions != null) {
+				Double r = currentExportRestictions.get(crop);
+				if (r != null) 
+					restiction = r;
+			}
+			
+			Double minLimit=null, maxLimit=null;
+			if (minMaxTradeLimits != null) {
+				Map<LimitType, Map<CropType, Double>> minMaxLimits = minMaxTradeLimits.get(currentTimestep.getYear());
+				minLimit = minMaxLimits.get(MinMaxNetImportManager.LimitType.MIN_LIMIT_TYPE).get(crop);
+				maxLimit = minMaxLimits.get(MinMaxNetImportManager.LimitType.MAX_LIMIT_TYPE).get(crop);
+			}
+			LogWriter.print(crop.toString());
+			importConstraints.put(crop, new TradeConstraint(baseTrade - changeDown, baseTrade + changeUp, restiction, minLimit, maxLimit));
 		}
 		
 		// Carbon import/export constraints TODO not used, might want in future
@@ -245,8 +267,9 @@ public class CountryAgent extends AbstractCountryAgent {
 				// assume 1tC/ha/year
 				double potentialYield = LandUseItem.getTotalLandCover(previousGamsRasterOutput.getLandUses().values(), LandCoverType.TIMBER_FOREST);
 				double currentDemand = currentWoodDemand.get(woodType);
-				double importsNeeded = Math.max(0, currentDemand - potentialYield * 0.5);
-				baseTrade = importsNeeded > 0 ? importsNeeded : baseTrade * 2;
+				double importsNeeded = Math.max(0, currentDemand - potentialYield * 3);
+				baseTrade = importsNeeded > 0 ? importsNeeded : baseTrade;
+				baseTrade = baseTrade < 0 ? baseTrade * 20 : baseTrade;
 			}
 			
 			double changeUp = 0.0;
@@ -267,20 +290,7 @@ public class CountryAgent extends AbstractCountryAgent {
 
 		return input;
 	}
-	
-	@Override
-	protected double getCommPriceFromCropPrice(CommodityType commodity) {
-		double commPricePlum = 0;
-		Map<CropType, Double> demandFract = currentDemandFract.get(commodity);
-		
-		for (CropType crop : commodity.getCropTypes()) {
-			double consumerCropPrice = currentCountryPrices.get(crop).getConsumerPrice();
-			commPricePlum += consumerCropPrice * demandFract.get(crop);  // weight price by base demand of each cereal crop
-		}
-		
-		return commPricePlum;
-	}
-	
+
 	@Override
 	protected CountryPrice createCountryPrices(CropType crop, GlobalPrice worldPrice) {
 		Map<CropType, CropUsageData> cropUsageMap = previousGamsRasterOutput.getCropUsageData();
diff --git a/src/ac/ed/lurg/country/CountryAgentManager.java b/src/ac/ed/lurg/country/CountryAgentManager.java
index 09fe057e7c0902394b8dfe912def28ccca1f55dc..257b80ee544c6d4c7a0353d7128070f5898d14d0 100644
--- a/src/ac/ed/lurg/country/CountryAgentManager.java
+++ b/src/ac/ed/lurg/country/CountryAgentManager.java
@@ -23,6 +23,9 @@ import ac.ed.lurg.landuse.CropUsageData;
 import ac.ed.lurg.landuse.IrrigationItem;
 import ac.ed.lurg.landuse.IrrigationRasterSet;
 import ac.ed.lurg.landuse.LandUseItem;
+import ac.ed.lurg.shock.ExportRestrictionManager;
+import ac.ed.lurg.shock.MinMaxNetImportManager;
+import ac.ed.lurg.shock.MinMaxNetImportManager.LimitType;
 import ac.ed.lurg.landuse.LccKey;
 import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.types.CropType;
@@ -38,6 +41,8 @@ public class CountryAgentManager {
 	private TradeManager tradeBarrierManager;
 	private SubsidyRateManager subsidyRateManager;
 	private InternationalMarket internationalMarket;
+	private ExportRestrictionManager exportRestrictionManager;
+	private MinMaxNetImportManager minMaxNetImportManager;
 	private CountryBoundaryRaster countryBoundaryRaster;
 	private RasterSet<IntegerRasterItem> clusterIdRaster;
 	private RasterSet<LandUseItem> globalLandUseRaster;
@@ -57,6 +62,8 @@ public class CountryAgentManager {
 		this.globalLandUseRaster = globalLandUseRaster;
 		tradeBarrierManager = new TradeManager(compositeCountryManager);
 		subsidyRateManager = new SubsidyRateManager(compositeCountryManager);
+		exportRestrictionManager = new ExportRestrictionManager();
+		minMaxNetImportManager = new MinMaxNetImportManager();
 		craftyManager = new CraftyProdManager();
 	}
 	
@@ -92,8 +99,10 @@ public class CountryAgentManager {
 				}
 				else {
 					Map<CropType, Double> subsidyRates = subsidyRateManager.getSubsidyRates(cc);
+					Map<Integer, Map<CropType, Double>> exportRestrictions = exportRestrictionManager.getShocksForCountry(cc);
+					Map<Integer, Map<LimitType, Map<CropType, Double>>> minMaxTradeLimits = minMaxNetImportManager.getMinMaxNetImportForCountry(cc);
 					
-					CountryAgent ca = new CountryAgent(demandManager, cc, initCountryLandUse, countryCommodityData, tradeBarriers, yieldClusters, subsidyRates, countryWoodData);
+					CountryAgent ca = new CountryAgent(demandManager, cc, initCountryLandUse, countryCommodityData, tradeBarriers, yieldClusters, subsidyRates, countryWoodData, exportRestrictions, minMaxTradeLimits);
 					gamsCountryAgents.add(ca);
 					countryAgents.add(ca);
 				}
diff --git a/src/ac/ed/lurg/country/CountryPrice.java b/src/ac/ed/lurg/country/CountryPrice.java
index 22c4587f279a44ce4ce4c2f7cd1913d5d681d84f..aac3fbc0e36c5177cc7d3276976d44618873d1f4 100644
--- a/src/ac/ed/lurg/country/CountryPrice.java
+++ b/src/ac/ed/lurg/country/CountryPrice.java
@@ -52,10 +52,10 @@ public class CountryPrice {
 				consumerPrice = importPrice;
 				break;
 			case WEIGHTEDIMPEXP:
-				consumerPrice = importPrice*importWeighting + getExportPrice()*(1-importWeighting);
+				consumerPrice = weightPrice(importWeighting, importPrice, getExportPrice());
 				break;
 			case WEIGHTEDINCLPRODCOSTS:
-				consumerPrice = importPrice*importWeighting + exportOrProdCost*(1-importWeighting);
+				consumerPrice = weightPrice(importWeighting, importPrice, exportOrProdCost);
 				break;
 			default:
 				consumerPrice = importPrice;
@@ -67,6 +67,13 @@ public class CountryPrice {
 				crop, importPrice, getExportPrice(), prodCost, importWeighting, consumerPrice));
 		return consumerPrice;
 	}
+
+	private double weightPrice(double weight, double iPrice, double ePrice) {
+		if (ePrice > iPrice)
+			return iPrice; // consumer price is not be allowed to rise above import price, you could just import rather than buying domestically produced products
+		else
+			return iPrice*weight + ePrice*(1-weight);
+	}
 	
 	private double calcProducerNetExportPrice(double exportTaxRate) {
 		if (ModelConfig.APPLY_EXPORT_TAXES)
diff --git a/src/ac/ed/lurg/country/GlobalPrice.java b/src/ac/ed/lurg/country/GlobalPrice.java
index f62455041703515ac5fdfb17e5f113ac7d6a5d48..552ba9df53bd246e2c20fe91db650ca2a567aabb 100644
--- a/src/ac/ed/lurg/country/GlobalPrice.java
+++ b/src/ac/ed/lurg/country/GlobalPrice.java
@@ -142,4 +142,8 @@ public class GlobalPrice implements Serializable {
 	public GlobalPrice createPriceAdjustedByFactor(double factor) {
 		return new GlobalPrice(timestep, exportPrice * factor, stockLevel, importAmount, exportAmountBeforeLoss, transportLosses, referencePrice);			
 	}
+
+	public void resetStock(Double stock) {
+		this.stockLevel = stock;
+	}
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/TradeConstraint.java b/src/ac/ed/lurg/country/TradeConstraint.java
index c314ab0c9ac00978ac4ae456caa38c9aa7c5ba6a..085065b6ce27e22607fcee4e2c9223df6a88d5a0 100644
--- a/src/ac/ed/lurg/country/TradeConstraint.java
+++ b/src/ac/ed/lurg/country/TradeConstraint.java
@@ -1,12 +1,42 @@
 package ac.ed.lurg.country;
 
+import ac.ed.lurg.utils.LogWriter;
+
 public class TradeConstraint {
 	private double minValue;
 	private double maxValue;
 	
-	public TradeConstraint(double minValue, double maxValue) {
-		this.minValue = minValue;
-		this.maxValue = maxValue;
+	public TradeConstraint(double min, double max) {
+		minValue = min;
+		maxValue = max;
+	}
+	
+	public TradeConstraint(double min, double max, double exportRetriction, Double minLimit, Double maxLimit) {
+		LogWriter.print(" TradeConstraint: min=" + min + ", max=" + max + ", exportRetriction=" + exportRetriction + ", minLimit=" + minLimit + ", maxLimit=" + maxLimit);
+
+		if (minLimit != null && min < minLimit) {
+			min = minLimit.doubleValue();
+			if (min > max)
+				max = min;
+		}
+		
+		if (maxLimit != null && max > maxLimit) {
+			max = maxLimit.doubleValue();
+			if (min > max)
+				min = max;
+		}
+		
+		minValue = applyExportRestriction(min, exportRetriction);
+		maxValue = applyExportRestriction(max, exportRetriction);
+		
+		LogWriter.println(" - resulted in minValue=" + minValue + ", maxValue=" + maxValue);
+	}
+	
+	private double applyExportRestriction(double amountBefore, double restrictionRate) {
+		if (amountBefore < 0 & restrictionRate > 0) // exporting and an export restriction is in place
+			return amountBefore * (1 - restrictionRate);
+		else
+			return amountBefore;
 	}
 	
 	public double getMinConstraint() {
@@ -16,4 +46,9 @@ public class TradeConstraint {
 	public double getMaxConstraint() {
 		return maxValue;
 	}
+
+	@Override
+	public String toString() {
+		return "TradeConstraint [minValue=" + minValue + ", maxValue=" + maxValue + "]";
+	}
 }
diff --git a/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
index 83faccc1589b99da4c420e16a9d90cd139ac2ced..3494f1559d73ccc99a91cdfbb0c832d9d164a678 100644
--- a/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
+++ b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
@@ -34,21 +34,17 @@ public class CraftyCountryAgent extends AbstractCountryAgent {
 		updateNetImportsFromProdAndDemand(currentProjectedDemand, currentDemandFract, cropUsageMap);
 	}
 	
-	@Override
- 	protected double getCommPriceFromCropPrice(CommodityType commodity) {
- 		double commPricePlum = 0;
- 		Map<CropType, Double> demandFract = currentDemandFract.get(commodity);
- 
- 		for (CropType crop : commodity.getCropTypes()) {
- 			commPricePlum += currentCountryPrices.get(crop).getImportPrice() * demandFract.get(crop);  // weight price by base demand of each cereal crop
- 		}
- 		return commPricePlum;
- 	}
-	
 	@Override
 	protected CountryPrice createCountryPrices(CropType crop, GlobalPrice worldPrice) {
-		// TODO Auto-generated method stub
-		return null;
+		CropUsageData cropUsage = cropUsageData.get(crop);
+
+		double weighting = Math.max(0, cropUsage.getShockedNetImports()/(cropUsage.getProductionExpected()+cropUsage.getShockedNetImports()));
+		double prodCost = cropUsage.getProdCostRate();
+		Double tb = tradeBarriers.get(crop);
+		double tradeBarrier = tb==null ? 0 : tb;
+		double exportTaxRateForCrop = CommodityType.CEREALS.getCropTypes().contains(crop) ? exportTaxRate : 0.0;
+		CountryPrice cp = new CountryPrice(crop, worldPrice, tradeBarrier, prodCost, weighting, exportTaxRateForCrop);
+		return cp;
 	}
 
 	public double getNetCarbonFlux() {
diff --git a/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java b/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java
index 814347b235f4ef041655d9c1b5900a6c237df9f1..c107b32939c5402e91b8e1c380d0f003e560ede7 100755
--- a/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java
@@ -1,181 +1,180 @@
-package ac.ed.lurg.country.gams;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Vector;
-
-import com.gams.api.GAMSDatabase;
-import com.gams.api.GAMSGlobals;
-import com.gams.api.GAMSGlobals.DebugLevel;
-import com.gams.api.GAMSJob;
-import com.gams.api.GAMSOptions;
-import com.gams.api.GAMSParameter;
-import com.gams.api.GAMSParameterRecord;
-import com.gams.api.GAMSVariable;
-import com.gams.api.GAMSVariableRecord;
-import com.gams.api.GAMSWorkspace;
-import com.gams.api.GAMSWorkspaceInfo;
-
-import ac.ed.lurg.ModelConfig;
-import ac.ed.lurg.types.CommodityType;
-import ac.ed.lurg.utils.LogWriter;
-
-public class GamsDemandOptimiser {
-
-	private GamsDemandInput inputData;
-
-	public GamsDemandOptimiser(GamsDemandInput inputData) {
-		this.inputData = inputData;
-	}
-	
-	public GamsDemandOutput getDemandPc() {
-		File workingDirectory = new File(ModelConfig.TEMP_DIR);
-		workingDirectory.mkdir();
-		GAMSWorkspaceInfo  wsInfo  = new GAMSWorkspaceInfo();
-		wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath());
-		LogWriter.println("debug5");
-		System.gc();
-		GAMSWorkspace ws = new GAMSWorkspace(wsInfo);
-		GAMSOptions opt = ws.addOptions();
-		GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.DEMAND_GAMS_MODEL);
-
-		GAMSDatabase inDB = ws.addDatabase();
-		setupPriceGdpDB(inDB);
-		ArrayList<GAMSDatabase> dbs = new ArrayList<GAMSDatabase>();
-		dbs.add(inDB);
-		opt.defines("gdx_prices_and_gdp", inDB.getName());
-
-		if (ModelConfig.ADJUST_DIET_PREFS) {
-			LogWriter.println("Adusting diet params");
-			GAMSDatabase originalParamDb = ws.addDatabaseFromGDX(new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath());
-			GAMSDatabase adjustedParamDb = ws.addDatabase(originalParamDb);
-			adjustDietParams(adjustedParamDb);
-			dbs.add(adjustedParamDb);
-			opt.defines("gdx_parameters", adjustedParamDb.getName());
-		}
-		else
-			opt.defines("gdx_parameters", new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath());
-
-		long startTime = System.currentTimeMillis();
-		LogWriter.println("Memory usage: GAMS Demand " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024.0*1024.0*1024.0));
-		gamsJob.run(opt, dbs.toArray(new GAMSDatabase[dbs.size()]));
-		LogWriter.println("Took " + (System.currentTimeMillis() - startTime) + " ms to run");
-		return handleResults(gamsJob.OutDB());
-	}
-
-	private void adjustDietParams(GAMSDatabase adjustedParamDb) {
-		// get original parameters
-		GAMSParameter tauParam = adjustedParamDb.getParameter("tau");
-		TauCalculationManager tauManager = TauCalculationManager.getInstance();
-		
-		for (GAMSParameterRecord rec : tauParam) {
-			String key = rec.getKeys()[0];
-			double initialTau = rec.getValue();
-			
-			CommodityType commodity = CommodityType.getForGamsName(key);
-			double adjusted = tauManager.getFinalTau(inputData.getYear(), initialTau, commodity);
-			LogWriter.println(String.format("%14s:  initialTau=%.6f, adjusted=%.6f", key, initialTau, adjusted));
-			rec.setValue(adjusted);
-		}
-	}
-
-	private void setupPriceGdpDB(GAMSDatabase inDB) {
-		GAMSParameter gdpPcP = inDB.addParameter("gdp_pc", 0);
-		double gdpPc = inputData.getGdpPc();
-		LogWriter.println(String.format("gdp_pc = %.5f", gdpPc)); 
-		setGamsParamValue(gdpPcP.addRecord(), gdpPc, 5);
-		GamsDemandOutput previousGamsDemands = inputData.getPreviousDemands();
-		
-		GAMSParameter prevUP = inDB.addParameter("previousU", 0);
-		double previousUtility = previousGamsDemands == null ? 0 : previousGamsDemands.getUtility();//-16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc);
-		LogWriter.println(String.format("previousU = %.5f, estimated = %.5f", previousUtility, -16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc))); 
-		setGamsParamValue(prevUP.addRecord(), previousUtility, 5);
-		
-		GAMSParameter maxIncomePropFoodSpendP = inDB.addParameter("maxIncomePropFoodSpend", 0);
-		setGamsParamValue(maxIncomePropFoodSpendP.addRecord(), ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND, 4);
-
-		LogWriter.println("\nPrice");	
-		GAMSParameter priceP = inDB.addParameter("price", 1);	
-		GAMSParameter prevSubsP = inDB.addParameter("previousSubs", 1);
-		GAMSParameter prevDiscP = inDB.addParameter("previousDisc", 1);
-
-		for (Map.Entry<CommodityType, Double> entry : inputData.getPrices().entrySet()) {
-			CommodityType comm = entry.getKey();
-			double priceComm = entry.getValue();
-			doCommodity(inDB, priceP, prevSubsP, prevDiscP, gdpPc, previousGamsDemands, comm, priceComm, 
-					comm==CommodityType.NONFOOD ? 5 : 0.05, comm==CommodityType.NONFOOD ? 0.5 : 0.05);
-		}
-	}
-
-	private void doCommodity(GAMSDatabase inDB, GAMSParameter priceP, GAMSParameter prevSubsP, GAMSParameter prevDiscP, double gdpPc, 
-			GamsDemandOutput previousGamsDemands, CommodityType comm, double price, double defaultSubs, double defaultDisc) {
-		
-		Vector<String> v = new Vector<String>();
-		v.add(comm.getGamsName());
-		double prevSubs = defaultSubs, prevDisc = defaultDisc;
-		
-		if (previousGamsDemands != null) {
-			GamsCommodityDemand demand = previousGamsDemands.getGamsDemands(comm);
-			prevSubs = demand.getSubsistence();
-			prevDisc = demand.getDiscretionary();
-		}
-		
-		LogWriter.println(String.format("%14s:  price=%.4f, previousSubs=%.4f, previousDisc=%.4f", comm.getGamsName(), price, prevSubs, prevDisc));
-		setGamsParamValue(priceP.addRecord(v), price, 4);
-		setGamsParamValue(prevSubsP.addRecord(v), prevSubs, 4);
-		setGamsParamValue(prevDiscP.addRecord(v), prevDisc, 4);
-	}
-
-	private double setGamsParamValue(GAMSParameterRecord param, double val, int places) {
-		double dOut = Math.round(val * Math.pow(10,places)) / Math.pow(10,places);
-		param.setValue(dOut);
-		return dOut;
-	}
-		
-	private GamsDemandOutput handleResults(GAMSDatabase outDB) {
-		int modelStatus = (int) outDB.getParameter("ms").findRecord().getValue();
-		String status = GAMSGlobals.ModelStat.lookup(modelStatus).toString();
-		LogWriter.println(String.format("\nDemand %s: Modelstatus (%d) %s, Solvestatus %s", inputData.getCountry(),
-				modelStatus, status, GAMSGlobals.SolveStat.lookup((int) outDB.getParameter("ss").findRecord().getValue()) ));
-		
-		GAMSVariable varU = outDB.getVariable("u");
-		double utility = varU.getFirstRecord().getLevel();
-		LogWriter.println(String.format("u = %.4f", utility)); 
-		
-		GAMSVariable varSubs = outDB.getVariable("SubsistenceC");
-		GAMSVariable varDisc = outDB.getVariable("DiscretionaryC");
-
-		HashSet<GamsCommodityDemand> demandMap = new HashSet<GamsCommodityDemand>();
-		Collection<CommodityType> unhandledComms = new ArrayList<CommodityType>(CommodityType.getAllFoodItems());
-		
-		for (GAMSVariableRecord rec : varSubs) {
-			String commodityName = rec.getKeys()[0];
-			CommodityType commodity = CommodityType.getForGamsName(commodityName, true);
-			
-			if (commodity == null)
-				LogWriter.println(String.format("Can't find commodity from Gams demand, so ignoring: " + commodityName)); 
-			else {
-				double subsGams = rec.getLevel();
-				double discGams = modelStatus==0 ? 0 : varDisc.findRecord(commodityName).getLevel();  //modelStatus==0 is optimiser not run, i.e. too low GDP
-				GamsCommodityDemand demand = new GamsCommodityDemand(commodity, subsGams, discGams,inputData.getKcalPerT(commodity));
-				LogWriter.println(demand.toString());
-				demandMap.add(demand);
-				unhandledComms.remove(commodity);
-			}
-		}
-		
-		for (CommodityType commodity : unhandledComms) {
-			LogWriter.println(commodity + " not found in Gams output so adding with zero demand"); 
-			demandMap.add(new GamsCommodityDemand(commodity, 0, 0, 0));
-		}
-		
-		GAMSParameter parConsumpFact = outDB.getParameter("desiredConsumpFactor");
-		double desiredConsumpFactor = parConsumpFact.getFirstRecord().getValue();
-		LogWriter.println(String.format("desiredConsumpFactor=%.4f", desiredConsumpFactor));
-		
-		return new GamsDemandOutput(status, demandMap, utility, desiredConsumpFactor);
-	}
-}
+package ac.ed.lurg.country.gams;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Vector;
+
+import com.gams.api.GAMSDatabase;
+import com.gams.api.GAMSGlobals;
+import com.gams.api.GAMSJob;
+import com.gams.api.GAMSOptions;
+import com.gams.api.GAMSParameter;
+import com.gams.api.GAMSParameterRecord;
+import com.gams.api.GAMSVariable;
+import com.gams.api.GAMSVariableRecord;
+import com.gams.api.GAMSWorkspace;
+import com.gams.api.GAMSWorkspaceInfo;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.types.CommodityType;
+import ac.ed.lurg.utils.LogWriter;
+
+public class GamsDemandOptimiser {
+
+	private GamsDemandInput inputData;
+
+	public GamsDemandOptimiser(GamsDemandInput inputData) {
+		this.inputData = inputData;
+	}
+	
+	public GamsDemandOutput getDemandPc() {
+		File workingDirectory = new File(ModelConfig.TEMP_DIR);
+		workingDirectory.mkdir();
+		GAMSWorkspaceInfo  wsInfo  = new GAMSWorkspaceInfo();
+		wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath());
+
+		GAMSWorkspace ws = new GAMSWorkspace(wsInfo);
+		GAMSOptions opt = ws.addOptions();
+		GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.DEMAND_GAMS_MODEL);
+		
+		GAMSDatabase inDB = ws.addDatabase();
+		setupPriceGdpDB(inDB);
+		ArrayList<GAMSDatabase> dbs = new ArrayList<GAMSDatabase>();
+		dbs.add(inDB);
+		opt.defines("gdx_prices_and_gdp", inDB.getName());
+
+		if (ModelConfig.ADJUST_DIET_PREFS) {
+			LogWriter.println("Adusting diet params");
+			GAMSDatabase originalParamDb = ws.addDatabaseFromGDX(new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath());
+			GAMSDatabase adjustedParamDb = ws.addDatabase(originalParamDb);
+			adjustDietParams(adjustedParamDb);
+			dbs.add(adjustedParamDb);
+			opt.defines("gdx_parameters", adjustedParamDb.getName());
+		}
+		else
+			opt.defines("gdx_parameters", new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath());
+
+		long startTime = System.currentTimeMillis();
+		LogWriter.println("Memory usage: GAMS Demand " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024.0*1024.0*1024.0));
+		gamsJob.run(opt, dbs.toArray(new GAMSDatabase[dbs.size()]));
+		
+		LogWriter.println("Took " + (System.currentTimeMillis() - startTime) + " ms to run");
+		return handleResults(gamsJob.OutDB());
+	}
+
+	private void adjustDietParams(GAMSDatabase adjustedParamDb) {
+		// get original parameters
+		GAMSParameter tauParam = adjustedParamDb.getParameter("tau");
+		TauCalculationManager tauManager = TauCalculationManager.getInstance();
+		
+		for (GAMSParameterRecord rec : tauParam) {
+			String key = rec.getKeys()[0];
+			double initialTau = rec.getValue();
+			
+			CommodityType commodity = CommodityType.getForGamsName(key);
+			double adjusted = tauManager.getFinalTau(inputData.getYear(), initialTau, commodity);
+			LogWriter.println(String.format("%14s:  initialTau=%.6f, adjusted=%.6f", key, initialTau, adjusted));
+			rec.setValue(adjusted);
+		}
+	}
+
+	private void setupPriceGdpDB(GAMSDatabase inDB) {
+		GAMSParameter gdpPcP = inDB.addParameter("gdp_pc", 0);
+		double gdpPc = inputData.getGdpPc();
+		LogWriter.println(String.format("gdp_pc = %.5f", gdpPc)); 
+		setGamsParamValue(gdpPcP.addRecord(), gdpPc, 5);
+		GamsDemandOutput previousGamsDemands = inputData.getPreviousDemands();
+		
+		GAMSParameter prevUP = inDB.addParameter("previousU", 0);
+		double previousUtility = previousGamsDemands == null ? 0 : previousGamsDemands.getUtility();//-16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc);
+		LogWriter.println(String.format("previousU = %.5f, estimated = %.5f", previousUtility, -16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc))); 
+		setGamsParamValue(prevUP.addRecord(), previousUtility, 5);
+		
+		GAMSParameter maxIncomePropFoodSpendP = inDB.addParameter("maxIncomePropFoodSpend", 0);
+		setGamsParamValue(maxIncomePropFoodSpendP.addRecord(), ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND, 4);
+
+		LogWriter.println("\nPrice");	
+		GAMSParameter priceP = inDB.addParameter("price", 1);	
+		GAMSParameter prevSubsP = inDB.addParameter("previousSubs", 1);
+		GAMSParameter prevDiscP = inDB.addParameter("previousDisc", 1);
+
+		for (Map.Entry<CommodityType, Double> entry : inputData.getPrices().entrySet()) {
+			CommodityType comm = entry.getKey();
+			double priceComm = entry.getValue();
+			doCommodity(inDB, priceP, prevSubsP, prevDiscP, gdpPc, previousGamsDemands, comm, priceComm, 
+					comm==CommodityType.NONFOOD ? 5 : 0.05, comm==CommodityType.NONFOOD ? 0.5 : 0.05);
+		}
+	}
+
+	private void doCommodity(GAMSDatabase inDB, GAMSParameter priceP, GAMSParameter prevSubsP, GAMSParameter prevDiscP, double gdpPc, 
+			GamsDemandOutput previousGamsDemands, CommodityType comm, double price, double defaultSubs, double defaultDisc) {
+		
+		Vector<String> v = new Vector<String>();
+		v.add(comm.getGamsName());
+		double prevSubs = defaultSubs, prevDisc = defaultDisc;
+		
+		if (previousGamsDemands != null) {
+			GamsCommodityDemand demand = previousGamsDemands.getGamsDemands(comm);
+			prevSubs = demand.getSubsistence();
+			prevDisc = demand.getDiscretionary();
+		}
+		
+		LogWriter.println(String.format("%14s:  price=%.4f, previousSubs=%.4f, previousDisc=%.4f", comm.getGamsName(), price, prevSubs, prevDisc));
+		setGamsParamValue(priceP.addRecord(v), price, 4);
+		setGamsParamValue(prevSubsP.addRecord(v), prevSubs, 4);
+		setGamsParamValue(prevDiscP.addRecord(v), prevDisc, 4);
+	}
+
+	private double setGamsParamValue(GAMSParameterRecord param, double val, int places) {
+		double dOut = Math.round(val * Math.pow(10,places)) / Math.pow(10,places);
+		param.setValue(dOut);
+		return dOut;
+	}
+		
+	private GamsDemandOutput handleResults(GAMSDatabase outDB) {
+		int modelStatus = (int) outDB.getParameter("ms").findRecord().getValue();
+		String status = GAMSGlobals.ModelStat.lookup(modelStatus).toString();
+		LogWriter.println(String.format("\nDemand %s: Modelstatus (%d) %s, Solvestatus %s", inputData.getCountry(),
+				modelStatus, status, GAMSGlobals.SolveStat.lookup((int) outDB.getParameter("ss").findRecord().getValue()) ));
+		
+		GAMSVariable varU = outDB.getVariable("u");
+		double utility = varU.getFirstRecord().getLevel();
+		LogWriter.println(String.format("u = %.4f", utility)); 
+		
+		GAMSVariable varSubs = outDB.getVariable("SubsistenceC");
+		GAMSVariable varDisc = outDB.getVariable("DiscretionaryC");
+
+		HashSet<GamsCommodityDemand> demandMap = new HashSet<GamsCommodityDemand>();
+		Collection<CommodityType> unhandledComms = new ArrayList<CommodityType>(CommodityType.getAllFoodItems());
+		
+		for (GAMSVariableRecord rec : varSubs) {
+			String commodityName = rec.getKeys()[0];
+			CommodityType commodity = CommodityType.getForGamsName(commodityName, true);
+			
+			if (commodity == null)
+				LogWriter.println(String.format("Can't find commodity from Gams demand, so ignoring: " + commodityName)); 
+			else {
+				double subsGams = rec.getLevel();
+				double discGams = modelStatus==0 ? 0 : varDisc.findRecord(commodityName).getLevel();  //modelStatus==0 is optimiser not run, i.e. too low GDP
+				GamsCommodityDemand demand = new GamsCommodityDemand(commodity, subsGams, discGams,inputData.getKcalPerT(commodity));
+				LogWriter.println(demand.toString());
+				demandMap.add(demand);
+				unhandledComms.remove(commodity);
+			}
+		}
+		
+		for (CommodityType commodity : unhandledComms) {
+			LogWriter.println(commodity + " not found in Gams output so adding with zero demand"); 
+			demandMap.add(new GamsCommodityDemand(commodity, 0, 0, 0));
+		}
+		
+		GAMSParameter parConsumpFact = outDB.getParameter("desiredConsumpFactor");
+		double desiredConsumpFactor = parConsumpFact.getFirstRecord().getValue();
+		LogWriter.println(String.format("desiredConsumpFactor=%.4f", desiredConsumpFactor));
+		
+		return new GamsDemandOutput(status, demandMap, utility, desiredConsumpFactor);
+	}
+}
diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
index 791d0744b2dda21b703aff8733b6ada845ecbc75..85208f09f0da4061d5115ab9fa24402ae0c761b5 100644
--- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
@@ -10,7 +10,6 @@ import java.util.Vector;
 import com.gams.api.GAMSDatabase;
 import com.gams.api.GAMSException;
 import com.gams.api.GAMSGlobals;
-import com.gams.api.GAMSGlobals.DebugLevel;
 import com.gams.api.GAMSGlobals.ModelStat;
 import com.gams.api.GAMSJob;
 import com.gams.api.GAMSOptions;
@@ -46,7 +45,7 @@ import ac.ed.lurg.yield.YieldResponsesItem;
 
 public class GamsLocationOptimiser {
 
-	private static final boolean DEBUG = false;
+	private static final boolean DEBUG = true;
 
 	private GamsLocationInput inputData;
 
@@ -55,16 +54,19 @@ public class GamsLocationOptimiser {
 	}
 
 	public GamsLocationOutput run() {
+
 		File workingDirectory = new File(ModelConfig.TEMP_DIR);
 		workingDirectory.mkdir();
+
 		GAMSWorkspaceInfo  wsInfo  = new GAMSWorkspaceInfo();
 		wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath());
-		LogWriter.println("debugb6");
-		System.gc();
+		//	wsInfo.setDebugLevel(DebugLevel.VERBOSE);
+
 		GAMSWorkspace ws = new GAMSWorkspace(wsInfo);
 		GAMSDatabase inDB = ws.addDatabase();
 
 		setupInDB(inDB);
+
 		GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.GAMS_MODEL);
 		GAMSOptions opt = ws.addOptions();
 
@@ -73,16 +75,14 @@ public class GamsLocationOptimiser {
 		opt.defines("gdxincname", inDB.getName());
 
 		long startTime = System.currentTimeMillis();
-		gamsJob.run(opt, inDB);	
+		gamsJob.run(opt, inDB);
 		
 		if (ModelConfig.CLEANUP_GAMS_DIR) 
 			cleanup(ws.workingDirectory());
 		
 		LogWriter.println("Took " + (System.currentTimeMillis() - startTime) + " ms to run");
-		
 
 		return handleResults(gamsJob.OutDB());
-		
 	}
 
 	private void setupInDB(GAMSDatabase inDB) {
@@ -195,11 +195,11 @@ public class GamsLocationOptimiser {
 
 		if (DEBUG) LogWriter.println("\nDemand: " + inputData.getCountryInput().getCountry() + " " + inputData.getTimestep().getYear());
 		GamsCountryInput countryInput = inputData.getCountryInput();
+
 		GAMSParameter demandP = inDB.addParameter("demand", 1);
 		addCommodityMapParm(demandP, countryInput.getProjectedDemand(), -1);
 		setGamsParamValue(demandP.addRecord(CropType.ENERGY_CROPS.getGamsName()), countryInput.getSecondGenBioenergyDemand(), -1);
 
-
 		GAMSParameter minCerealFracP = inDB.addParameter("minDemandPerCereal", 1);
 		GAMSParameter minOilcropsFracP = inDB.addParameter("minDemandPerOilcrop", 1);
 		if (DEBUG) LogWriter.println("\nMinDemandFractions");
@@ -287,7 +287,7 @@ public class GamsLocationOptimiser {
 			double minTrade = iec.getMinConstraint();
 			double maxTrade = iec.getMaxConstraint();
 			double importPrice = gp.getImportPrice();
-			double exportPrice = gp.getExportPrice();
+			double exportPrice = gp.getProducerNetExportPrice();
 			
 			CropUsageData cu = countryInput.getPreviousCropUsageData().get(crop);
 			double netImports = cu.getNetImportsExpected();
@@ -311,7 +311,6 @@ public class GamsLocationOptimiser {
 			setGamsParamValue(previousMonogastricFeedP.addRecord(crop.getGamsName()), monogastricFeed, 3);
 			setGamsParamValue(seedAndWasteRateP.addRecord(crop.getGamsName()), seedAndWasteRate, 3);
 		}
-		
 
 		//below in case running without shocks to use original values in model config 
 		
@@ -479,7 +478,7 @@ public class GamsLocationOptimiser {
 		GAMSParameter parmProdCost = outDB.getParameter("totalProdCost");
 		GAMSParameter parmCroplandArea = outDB.getParameter("totalCropland");
 		GAMSParameter parmTotalArea = outDB.getParameter("totalArea");
-		GAMSParameter parmProdShock = outDB.getParameter("productionShock");		
+		GAMSParameter parmProdShock = outDB.getParameter("productionShock");
 		
 		double totalCropArea = 0;
 		double totalPastureArea = 0;
diff --git a/src/ac/ed/lurg/forestry/WoodYieldItem.java b/src/ac/ed/lurg/forestry/WoodYieldItem.java
index 9027b28f303c6d95c629f4481c8490e7d737e0c5..f9f0459579df3cfd35c6869417b21b132070ff83 100644
--- a/src/ac/ed/lurg/forestry/WoodYieldItem.java
+++ b/src/ac/ed/lurg/forestry/WoodYieldItem.java
@@ -45,6 +45,13 @@ public class WoodYieldItem implements RasterItem {
 	}
 	
 	public void calcRotationData(Map<LccKey, Double[]> woodYields, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep, double woodPrice) {
+		
+		if (!ModelConfig.IS_FORESTRY_ON) {
+			optimalRotation = 0;
+			yieldAtRotation = 0;
+			return;
+		}
+			
 		// Optimal rotation
 		List<Double> levArr = new ArrayList<Double>();
 		
diff --git a/src/ac/ed/lurg/landuse/LandCoverTile.java b/src/ac/ed/lurg/landuse/LandCoverTile.java
index 776add086d3443d9de4ec61c5b3cc2f54cb2d249..250e37f0d59584995c2d30fc3ac8b9209cb760e0 100644
--- a/src/ac/ed/lurg/landuse/LandCoverTile.java
+++ b/src/ac/ed/lurg/landuse/LandCoverTile.java
@@ -42,15 +42,14 @@ public class LandCoverTile implements Serializable, InterpolatingRasterItem<Land
 		areas.put(key, prevArea + a);
 	}
 	
+	// This is used during calibration to preserve Land cover age distribution
 	public void addArea(LandProtectionType lpType, double a) {
 		double totalArea = getTotalArea(lpType);
 		if (totalArea == 0) {
-			for (int age = 0; age <= 150; age+=10) {
-				addArea(lpType, age, a/16); // assuming even distribution
-			}
+			addArea(lpType, 0, a); // assuming all age=0 if no previous land cover
 			
 		} else {
-			double factor = (a + totalArea) / totalArea;
+			double factor = (a + totalArea) / totalArea; // proportional allocation
 			for (LandKey key : areas.keySet()) {
 				if (key.getLpType().equals(lpType)) {
 					double prevArea = getArea(key);
@@ -81,7 +80,7 @@ public class LandCoverTile implements Serializable, InterpolatingRasterItem<Land
 		setArea(key, prevArea - a);
 	}
 
-	public void removeArea(LandProtectionType lpType, double a) { // Subtracts area uniformly
+	public void removeArea(LandProtectionType lpType, double a) { // Subtracts area proportionally
 		if (a == 0)
 			return;
 		double totalArea = getTotalArea(lpType);
@@ -106,19 +105,6 @@ public class LandCoverTile implements Serializable, InterpolatingRasterItem<Land
 		return area;
 	}
 
-	public void resetAreaForAge(LandProtectionType lpType, int age) {
-		LandKey key = new LandKey(lpType, age);
-		double a = getArea(key);
-		areas.remove(key);
-		setArea(lpType, 0, a);
-	}
-	
-	public void resetAreaForAge(LandProtectionType lpType, int age, double a) {
-		a = Math.min(getArea(lpType, age), a);
-		removeArea(lpType, age, a);
-		addArea(lpType, 0, a);
-	}
-	
 	public void removeAllArea(LandProtectionType lpType) {
 		Map<LandKey, Double> newAreas = new HashMap<LandKey, Double>();
 		for (LandKey key : areas.keySet()) {
diff --git a/src/ac/ed/lurg/landuse/WoodUsageData.java b/src/ac/ed/lurg/landuse/WoodUsageData.java
index d650ef5d85de31830f3d017d73e287c89a9e4d91..cddab4878b87b12ff86f674346c2ee4bd8599c3f 100644
--- a/src/ac/ed/lurg/landuse/WoodUsageData.java
+++ b/src/ac/ed/lurg/landuse/WoodUsageData.java
@@ -6,18 +6,18 @@ public class WoodUsageData implements Serializable {
 
 	private static final long serialVersionUID = -3329080279189782763L;
 	
-	private double harvest;
+	private double rotaHarvest;
 	private double netImport;
 	private double lucHarvest;
 	
 	public WoodUsageData(double harvest, double netImport, double lucHarvest) {
-		this.harvest = harvest;
+		this.rotaHarvest = harvest;
 		this.netImport = netImport;
 		this.lucHarvest = lucHarvest;
 	}
 
 	public double getHarvest() {
-		return harvest;
+		return rotaHarvest;
 	}
 
 	public double getNetImport() {
diff --git a/src/ac/ed/lurg/output/LpjgOutputer.java b/src/ac/ed/lurg/output/LpjgOutputer.java
index 4d45187bd48669f533f449fb18614e314e3c5231..725f2976b10b465e23a8c308aff64b3173ac635f 100644
--- a/src/ac/ed/lurg/output/LpjgOutputer.java
+++ b/src/ac/ed/lurg/output/LpjgOutputer.java
@@ -125,7 +125,7 @@ public class LpjgOutputer extends AbstractLandUseOutputer {
 		try {
 			String landCoverFileName = outputDir.getPath() + File.separator + "LandCoverFract.txt";
 			landCoverWriter = new BufferedWriter(new FileWriter(landCoverFileName, false));
-			landCoverWriter.write("Lon Lat Year CROPLAND PASTURE NATURAL BARREN URBAN");
+			landCoverWriter.write("Lon Lat Year CROPLAND PASTURE TIMBER CARBON NATURAL BARREN URBAN");
 			landCoverWriter.newLine();
 
 			for (Entry<RasterKey, LandUseItem> entry : landUseRaster.entrySet()) {
@@ -143,11 +143,12 @@ public class LpjgOutputer extends AbstractLandUseOutputer {
 				
 				double crop = item.getLandCoverFract(LandCoverType.CROPLAND);
 				double pasture = item.getLandCoverFract(LandCoverType.PASTURE);
-				double forest = item.getLandCoverFract(LandCoverType.TIMBER_FOREST) + item.getLandCoverFract(LandCoverType.CARBON_FOREST);
+				double timberForest = item.getLandCoverFract(LandCoverType.TIMBER_FOREST);
+				double carbonForest = item.getLandCoverFract(LandCoverType.CARBON_FOREST);
 				double otherNatural = item.getLandCoverFract(LandCoverType.NATURAL);
 				double barren = item.getLandCoverFract(LandCoverType.BARREN);
 				double urban = item.getLandCoverFract(LandCoverType.URBAN);
-				landCoverWriter.write(String.format("%.2f %.2f %d %.14f %.14f %.14f %.14f %.14f", lat, lon, year, crop, pasture, (forest+otherNatural), barren, urban));
+				landCoverWriter.write(String.format("%.2f %.2f %d %.14f %.14f %.14f %.14f %.14f", lat, lon, year, crop, pasture, timberForest, carbonForest, otherNatural, barren, urban));
 				landCoverWriter.newLine();
 			}
 		}
diff --git a/src/ac/ed/lurg/shock/ExportRestrictionManager.java b/src/ac/ed/lurg/shock/ExportRestrictionManager.java
index e9b808b7ff207f0604f27fa55b7d7bf28c6288b3..de7d3d56f5a22152eaf30096e9a36a2c61c71714 100644
--- a/src/ac/ed/lurg/shock/ExportRestrictionManager.java
+++ b/src/ac/ed/lurg/shock/ExportRestrictionManager.java
@@ -6,9 +6,9 @@ import java.util.List;
 import java.util.Map;
 
 import ac.ed.lurg.ModelConfig;
-import ac.ed.lurg.Timestep;
 import ac.ed.lurg.country.CompositeCountry;
 import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.utils.LazyHashMap;
 import ac.ed.lurg.utils.LogWriter;
 import ac.ed.lurg.utils.StringTabularReader;
 
@@ -18,7 +18,7 @@ public class ExportRestrictionManager {
 	public ExportRestrictionManager() {
 
 		String shockFile = ModelConfig.EXPORT_RESTRICTIONS_FILE;
-		exportShockReader = new StringTabularReader(",", new String[]{"year", "country", "crop", "shockFactor"});
+		exportShockReader = new StringTabularReader(",", new String[]{"year", "compositeCountry", "crop", "shockFactor"});
 
 		if (new File(shockFile).isFile()) 
 			exportShockReader.read(shockFile);
@@ -26,24 +26,33 @@ public class ExportRestrictionManager {
 			LogWriter.println("Can't find a export shock file (" +shockFile + "), so will not apply export restriction");
 	}
 	
-	public Map<CropType, Double> getShocksForCountry(Timestep timestep, CompositeCountry cc) {
-		Map<CropType, Double> shocks = new HashMap<CropType, Double>();
-
+	@SuppressWarnings("serial")
+	public Map<Integer, Map<CropType, Double>> getShocksForCountry(CompositeCountry cc) {
 		Map<String, String> queryMap = new HashMap<String, String>();
-		queryMap.put("year", Integer.toString(timestep.getYear()));
-		queryMap.put("country", cc.getName());
-		
+		queryMap.put("compositeCountry", cc.getName());
 		List<Map<String, String>> rows = exportShockReader.query(queryMap);
-		for (Map<String, String> row : rows) {
-		String cropTypeS = row.get("crop");
-		String shockFactorS = row.get("shockFactor");
-		CropType crop = CropType.getForFaoName(cropTypeS);
-		Double shockFactor = Double.valueOf(shockFactorS);
-
-		LogWriter.println("Got export shock at " + timestep + ": " + cc.getName() + " " + cropTypeS + " " + shockFactorS);
-		
-		shocks.put(crop, shockFactor);
+		LazyHashMap<Integer, Map<CropType, Double>> shocks = null;
+
+		if (rows.size() > 0) {
+			shocks = new LazyHashMap<Integer, Map<CropType, Double>>() {
+				protected Map<CropType, Double> createValue() { return new HashMap<CropType, Double>(); }
+			};
+
+			for (Map<String, String> row : rows) {
+				String cropTypeS = row.get("crop");
+				String shockFactorS = row.get("shockFactor");
+				String yearS = row.get("year");
+				LogWriter.println("Got export shock " + cc.getName() + " : " + yearS + " " + cropTypeS + " " + shockFactorS);
+				
+				CropType crop = CropType.getForGamsName(cropTypeS);
+				Double shockFactor = Double.valueOf(shockFactorS);
+				Integer year = Integer.valueOf(yearS);
+				
+				Map<CropType, Double> shocksCC = shocks.lazyGet(year);
+				shocksCC.put(crop, shockFactor);
+			}
 		}
+		
 		return shocks;
 	}
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/shock/MinMaxNetImportManager.java b/src/ac/ed/lurg/shock/MinMaxNetImportManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c4fda65de6ba143d3ae56687a34bcb42f4fecfb
--- /dev/null
+++ b/src/ac/ed/lurg/shock/MinMaxNetImportManager.java
@@ -0,0 +1,89 @@
+package ac.ed.lurg.shock;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.country.CompositeCountry;
+import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.utils.LazyHashMap;
+import ac.ed.lurg.utils.LogWriter;
+import ac.ed.lurg.utils.StringTabularReader;
+
+public class MinMaxNetImportManager {
+	private StringTabularReader minMaxTradeReader;
+
+	public enum LimitType {
+		MIN_LIMIT_TYPE("min"),
+		MAX_LIMIT_TYPE("max");
+
+		private String limitType;
+
+		LimitType(String limitType) {
+			this.limitType = limitType;
+		}
+
+		public static LimitType findByName(String str){
+		    for(LimitType p : values()){
+		        if( p.limitType.equals(str)){
+		            return p;
+		        }
+		    }
+		    
+		    LogWriter.printlnError("No LimitType found for " + str);
+		    return null;
+		}
+	}
+
+	public MinMaxNetImportManager() {
+
+		String fileName = ModelConfig.MIN_MAX_TRADE_FILE;
+		minMaxTradeReader = new StringTabularReader(",", new String[]{"year", "compositeCountry", "crop", "limitType", "netImportValue"});
+
+		if (new File(fileName).isFile()) 
+			minMaxTradeReader.read(fileName);
+		else
+			LogWriter.println("Can't find a min_max_trade file (" +fileName + "), so will not apply trade limits.");
+	}
+	
+	@SuppressWarnings("serial")
+	public Map<Integer, Map<LimitType, Map<CropType, Double>>> getMinMaxNetImportForCountry(CompositeCountry cc) {
+		Map<String, String> queryMap = new HashMap<String, String>();
+		queryMap.put("compositeCountry", cc.getName());
+
+		List<Map<String, String>> rows = minMaxTradeReader.query(queryMap);
+		LazyHashMap<Integer, Map<LimitType, Map<CropType, Double>>> tradeLimits = null;
+
+		if (rows.size() > 0) {
+			tradeLimits = new LazyHashMap<Integer, Map<LimitType, Map<CropType, Double>>>() {
+				protected Map<LimitType, Map<CropType, Double>> createValue() { 
+					Map<LimitType, Map<CropType, Double>> theMap = new HashMap<LimitType, Map<CropType, Double>>();
+					theMap.put(LimitType.MIN_LIMIT_TYPE, new HashMap<CropType, Double>());
+					theMap.put(LimitType.MAX_LIMIT_TYPE, new HashMap<CropType, Double>());
+					return theMap;
+				}
+			};
+
+			for (Map<String, String> row : rows) {
+				String cropTypeS = row.get("crop");
+				String netImportValueS = row.get("netImportValue");
+				String yearS = row.get("year");
+				String limitTypeS = row.get("limitType");
+				LogWriter.println("Got export " + limitTypeS + " trade limit " + cc.getName() + " : " + yearS + " " + cropTypeS + " " + netImportValueS);
+				
+				CropType crop = CropType.getForGamsName(cropTypeS);
+				Double netImportValue = Double.valueOf(netImportValueS);
+				Integer year = Integer.valueOf(yearS);
+				LimitType limitType = LimitType.findByName(limitTypeS);
+				
+				Map<LimitType, Map<CropType, Double>> minMaxlimits = tradeLimits.lazyGet(year);
+				Map<CropType, Double> limits = minMaxlimits.get(limitType);
+				limits.put(crop, netImportValue);
+			}
+		}
+		
+		return tradeLimits;
+	}
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/types/CommodityType.java b/src/ac/ed/lurg/types/CommodityType.java
index 5c3b8e5e282866cd2c1bd8561498dc9f74ce824d..15dd1da8eced826397aec6c7b596319e73bd5481 100644
--- a/src/ac/ed/lurg/types/CommodityType.java
+++ b/src/ac/ed/lurg/types/CommodityType.java
@@ -12,34 +12,32 @@ public enum CommodityType {
 
 	//processedFull$cb_fs[is.na(fcr) & Country=="World" & Year == 2010, list(energy=sum(energy_kcal_pc*365/1000000*country_data[Country=="World" & Year == 2010]$population, na.rm=TRUE), food=sum(food)), by="plumDemandItem"][, list(plumDemandItem, energy/food)]
 	//processedFull$cb_fs[!is.na(fcr) & Country=="World" & Year == 2010, list(energy=sum(energy_kcal_pc*365/1000000*country_data[Country=="World" & Year == 2010]$population, na.rm=TRUE), food=sum(food*fcr)), by="plumDemandItem"][, list(plumDemandItem, energy/food)]
-	CEREALS("Cereals", "cereals", false, new CropType[]{CropType.WHEAT, CropType.MAIZE, CropType.RICE}, new Double[]{0.385, 0.171, 0.444}, ModelConfig.CEREALS_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_CEREALS, 409.0),
-	OILCROPSPULSES("OilcropsPulses", "oilcropspulses", false, new CropType[]{CropType.OILCROPS, CropType.PULSES}, new Double[]{0.815, 0.185}, ModelConfig.OILCROPSPULSES_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_OILCROPS_PULSES, 23.0),
-	STARCHY_ROOTS("Starchy Roots", "starchyRoots", false, new CropType[]{CropType.STARCHY_ROOTS}, new Double[]{1.0}, ModelConfig.STARCHY_ROOTS_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_STARCHYROOTS, 14.0),
-	MONOGASTRICS("Monogastrics", "monogastrics", true, new CropType[]{CropType.MONOGASTRICS}, new Double[]{1.0}, Double.NaN, ModelConfig.INITAL_DEMAND_PRICE_MONOGASTRICS, 3.0),
-	RUMINANTS("Ruminants", "ruminants", true, new CropType[]{CropType.RUMINANTS}, new Double[]{1.0}, Double.NaN, ModelConfig.INITAL_DEMAND_PRICE_RUMINANTS, 20.0),
-	FRUITVEG("FruitVeg", "fruitveg", false, new CropType[]{CropType.FRUITVEG}, new Double[]{1.0}, ModelConfig.FRUITVEG_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_FRUITVEG, 21.0),
-	SUGAR("Sugar", "sugar", false, new CropType[]{CropType.SUGAR}, new Double[]{1.0}, ModelConfig.SUGAR_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_SUGAR, 35.0),
-	NONFOOD("Nonfood", "nonfood", false, new CropType[]{}, new Double[]{}, Double.NaN, Double.NaN, 0.0);
+	CEREALS("Cereals", "cereals", false, new CropType[]{CropType.WHEAT, CropType.MAIZE, CropType.RICE}, new Double[]{0.385, 0.171, 0.444}, ModelConfig.INITAL_DEMAND_PRICE_CEREALS, 409.0),
+	OILCROPSPULSES("OilcropsPulses", "oilcropspulses", false, new CropType[]{CropType.OILCROPS, CropType.PULSES}, new Double[]{0.815, 0.185}, ModelConfig.INITAL_DEMAND_PRICE_OILCROPS_PULSES, 23.0),
+	STARCHY_ROOTS("Starchy Roots", "starchyRoots", false, new CropType[]{CropType.STARCHY_ROOTS}, new Double[]{1.0}, ModelConfig.INITAL_DEMAND_PRICE_STARCHYROOTS, 14.0),
+	MONOGASTRICS("Monogastrics", "monogastrics", true, new CropType[]{CropType.MONOGASTRICS}, new Double[]{1.0}, ModelConfig.INITAL_DEMAND_PRICE_MONOGASTRICS, 3.0),
+	RUMINANTS("Ruminants", "ruminants", true, new CropType[]{CropType.RUMINANTS}, new Double[]{1.0}, ModelConfig.INITAL_DEMAND_PRICE_RUMINANTS, 20.0),
+	FRUITVEG("FruitVeg", "fruitveg", false, new CropType[]{CropType.FRUITVEG}, new Double[]{1.0}, ModelConfig.INITAL_DEMAND_PRICE_FRUITVEG, 21.0),
+	SUGAR("Sugar", "sugar", false, new CropType[]{CropType.SUGAR}, new Double[]{1.0}, ModelConfig.INITAL_DEMAND_PRICE_SUGAR, 35.0),
+	NONFOOD("Nonfood", "nonfood", false, new CropType[]{}, new Double[]{}, Double.NaN, 0.0);
 
 	private String faoName;
 	private String gamsName;
 	private boolean isAnimalProduct;
 	private Collection<CropType> cropTypes;
-	private double meatSubstitutionProportion;
 	private double initialPrice;
 	private double minFAOCalorieIntake;
-	
+
 	CommodityType (String faoName, String gamsName, boolean isAnimalProduct, CropType[] cropTypeMapping, Double[] defaultDemandFract, 
-			double meatSubstitutionProportion, double initialPrice, double minFAOCalorieIntake) {
+			double initialPrice, double minFAOCalorieIntake) {
 		this.faoName = faoName;
 		this.gamsName = gamsName;
 		this.isAnimalProduct = isAnimalProduct;
 		this.cropTypes = Arrays.asList(cropTypeMapping);
-		this.meatSubstitutionProportion = meatSubstitutionProportion;
 		this.initialPrice = initialPrice;
 		this.minFAOCalorieIntake = minFAOCalorieIntake;
 	}
-	
+
 	private static final Map<String, CommodityType> faoNameCache = new HashMap<String, CommodityType>();
 	private static final Map<String, CommodityType> gamsNameCache = new HashMap<String, CommodityType>();
 	private static final Collection<CommodityType> foodsToConsider = new HashSet<CommodityType>();
@@ -47,7 +45,7 @@ public enum CommodityType {
 		for (CommodityType c : values()) {
 			faoNameCache.put(c.getFaoName(), c);
 			gamsNameCache.put(c.getGamsName(), c);
-			
+
 			if (!c.equals(NONFOOD))
 				foodsToConsider.add(c);
 		}
@@ -58,23 +56,23 @@ public enum CommodityType {
 
 		if (commodity == null) 
 			throw new RuntimeException("Can't find CommodityType for faoName: " + faoName);
-		
+
 		return commodity;
 	}
-	
+
 	public static CommodityType getForGamsName(String gamsName) {
 		return getForGamsName(gamsName, false);
 	}
-	
+
 	public static CommodityType getForGamsName(String gamsName, boolean allowNulls) {
 		CommodityType commodity = gamsNameCache.get(gamsName);
 
 		if (commodity == null & !allowNulls) 
 			throw new RuntimeException("Can't find CommodityType for gamsName: " + gamsName);
-		
+
 		return commodity;
 	}
-	
+
 	public static Collection<CommodityType> getAllFoodItems() {
 		return foodsToConsider;
 	}
@@ -82,7 +80,7 @@ public enum CommodityType {
 	public String getFaoName() {
 		return faoName;
 	}
-	
+
 	public String getGamsName() {
 		return gamsName;
 	}
@@ -90,19 +88,15 @@ public enum CommodityType {
 	public boolean isAnimalProduct() {
 		return isAnimalProduct;
 	}
-	
-	public double getMeatSubstitutionProportion(){
-		return meatSubstitutionProportion;
-	}
-	
+
 	public Collection<CropType> getCropTypes(){
 		return cropTypes;
 	}
-	
+
 	public double getDefaultInitialPrice() {
 		return initialPrice;
 	}
-	
+
 	public double getMinFAOCalorieIntake() {
 		return minFAOCalorieIntake;
 	}
diff --git a/src/ac/ed/lurg/types/ForestType.java b/src/ac/ed/lurg/types/ForestType.java
deleted file mode 100644
index e591ca244a403ede79d387676668e9ad4373ba75..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/types/ForestType.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package ac.ed.lurg.types;
-
-public enum ForestType {
-	TIMBER_FOREST("timberForest"),
-	CARBON_FOREST("carbonForest");
-	
-	private String name;
-	
-	ForestType(String name) {
-		this.name = name;
-	}
-	
-	public String getName() {
-		return name;
-	}
-}
diff --git a/src/ac/ed/lurg/types/GamsLandCoverType.java b/src/ac/ed/lurg/types/GamsLandCoverType.java
deleted file mode 100644
index d656e10b2d8087b137e1ecf2063c29afaf46c8b2..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/types/GamsLandCoverType.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package ac.ed.lurg.types;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import ac.ed.lurg.utils.LogWriter;
-
-public enum GamsLandCoverType {
-	TIMBER_FOREST("timberForest"),
-	CARBON_FOREST("carbonForest"),
-	NATURAL("natural"),
-	PASTURE("pasture"),
-	CROPLAND("cropland");
-	
-	private String name;
-	
-	GamsLandCoverType(String name) {
-		this.name = name;
-	}
-	
-	public String getName() {
-		return name;
-	}
-	
-	private static final Map<String, GamsLandCoverType> nameCache = new HashMap<String, GamsLandCoverType>();
-    static {
-        for (GamsLandCoverType c : values()) {
-        	nameCache.put(c.getName(), c);
-       }
-    }
-    
-	private static GamsLandCoverType getFromCache(Map<String, GamsLandCoverType> cache, String name) {
-		GamsLandCoverType type = cache.get(name);
-		
-		if (type == null)
-			LogWriter.printlnError("Can't find Item for " + name);
-		
-		return type;
-	}
-    
-	public static GamsLandCoverType getForName(String name) {
-		return getFromCache(nameCache, name);
-	}
-}
-
diff --git a/src/ac/ed/lurg/types/NonCropType.java b/src/ac/ed/lurg/types/NonCropType.java
deleted file mode 100644
index ad5f9a6dc209daea30c1f75ce516b28a0e0140b7..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/types/NonCropType.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package ac.ed.lurg.types;
-
-import java.util.Collection;
-import java.util.HashSet;
-
-public enum NonCropType {
-	TIMBER_FOREST("timberForest", true, true),
-	CARBON_FOREST("carbonForest", true, true),
-	NATURAL("natural", true, false);
-
-	private String gamsName;
-	private boolean isWoodProducing;
-	private boolean isManagedForest;
-	
-	private NonCropType (String gamsName, boolean isWoodProducing, boolean isManagedForest) {
-		this.gamsName = gamsName;
-		this.isWoodProducing = isWoodProducing;
-		this.isManagedForest = isManagedForest;
-	}
-	
-	public String getGamsName() {
-		return gamsName;
-	}
-	
-	public boolean isWoodProducing() {
-		return isWoodProducing;
-	}
-	
-	public boolean isManagedForest() {
-		return isManagedForest;
-	}
-	
-	public static Collection<NonCropType> getWoodProducingTypes() {
-		Collection<NonCropType> lu = new HashSet<NonCropType>();
-		
-		for (NonCropType c : values())
-			if (c.isWoodProducing)
-				lu.add(c);
-		
-		return lu;
-	}
-	
-	public static Collection<NonCropType> getManagedForestTypes() {
-		Collection<NonCropType> lu = new HashSet<NonCropType>();
-		
-		for (NonCropType c : values())
-			if (c.isManagedForest)
-				lu.add(c);
-		
-		return lu;
-	}
-
-}
diff --git a/src/ac/ed/lurg/types/WoodType.java b/src/ac/ed/lurg/types/WoodType.java
index a840707636a1e31694ead55eeb82b6978e348293..e32c8dee2db391488ecf26208588cffe77f90899 100644
--- a/src/ac/ed/lurg/types/WoodType.java
+++ b/src/ac/ed/lurg/types/WoodType.java
@@ -5,7 +5,7 @@ import java.util.Map;
 import ac.ed.lurg.ModelConfig;
 
 public enum WoodType {
-	ROUNDWOOD("roundwood", ModelConfig.IND_ROUNDWOOD_DEMAND_ELASTICITY),
+	IND_ROUNDWOOD("roundwood", ModelConfig.IND_ROUNDWOOD_DEMAND_ELASTICITY),
 	FUELWOOD("fuelwood", ModelConfig.FUELWOOD_DEMAND_ELASTICITY);
 	
 	private String name;
diff --git a/src/ac/ed/lurg/utils/BilinearInterpolator.java b/src/ac/ed/lurg/utils/BilinearInterpolator.java
deleted file mode 100644
index bbe4b68144133845e7064e58e540e5030f52818a..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/utils/BilinearInterpolator.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package ac.ed.lurg.utils;
-
-public final class BilinearInterpolator {
-	/* 
-	 * coordinates to interpolate (x, y)
-	 * known values q11 = f(x1, y1), q12 = f(x1, y2), q21 = f(x2, y1), q22 = f(x2, y2)
-	*/
-	public static double interpolate(double x, double y, double x1, double x2, double y1, double y2, double q11, double q12, double q21, double q22) {
-		double fxy1 = ((x2 -  x) / (x2 - x1)) * q11 + ((x - x1) / (x2 - x1)) * q21;
-		double fxy2 = ((x2 -  x) / (x2 - x1)) * q12 + ((x - x1) / (x2 - x1)) * q22;
-		double fxy = ((y2 - y) / (y2 - y1)) * fxy1 + ((y - y1) / (y2 - y1)) * fxy2;
-		return fxy;
-	}
-	
-	// Version with integer coordinates
-	public static double interpolate(int x, int y, int x1, int x2, int y1, int y2, double q11, double q12, double q21, double q22) {
-		double fxy1 = ((x2 -  x) / (x2 - x1)) * q11 + ((x - x1) / (x2 - x1)) * q21;
-		double fxy2 = ((x2 -  x) / (x2 - x1)) * q12 + ((x - x1) / (x2 - x1)) * q22;
-		double fxy = ((y2 - y) / (y2 - y1)) * fxy1 + ((y - y1) / (y2 - y1)) * fxy2;
-		return fxy;
-	}
-}
diff --git a/src/ac/ed/lurg/utils/DoubleMap.java b/src/ac/ed/lurg/utils/DoubleMap.java
deleted file mode 100644
index ed3a2d10be74dfa30eaa26671bd942d8c1a905a8..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/utils/DoubleMap.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package ac.ed.lurg.utils;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-public class DoubleMap<K, L, V> implements Serializable {
-	private static final long serialVersionUID = 309214428660247740L;
-	
-	// Stores a double mapped by two consecutive keys
-	private Map<K, Map<L, Double>> theMap; 
-	
-	public DoubleMap() {
-		this.theMap = new HashMap<K, Map<L, Double>>(); 
-	}
-	
-	public void put(K key1, L key2, double value) {
-		if (theMap.containsKey(key1)) { // check if inner map already exists
-			theMap.get(key1).put(key2, value); // adds new entry or overwrites existing if exists
-		} else { // add new complete entry
-			Map<L, Double> innerMap = new HashMap<L, Double>();
-			innerMap.put(key2, value);
-			theMap.put(key1, innerMap);
-		}
-	}
-
-	public Double get(K key1, L key2) {
-		if (theMap.containsKey(key1)) { // check if first key exits
-			return theMap.get(key1).get(key2); // will return null if second key doesn't exist
-		} else {
-			return null; // will return null if first key doesn't exist
-		}
-	}
-	
-	public Map<L, Double> getInnerMap(K key) {
-		return theMap.get(key);
-
-	}
-	
-	public Map<K, Map<L, Double>> getMap() {
-		return theMap;
-	}
-	
-	public void addTo(K key1, L key2, double value) {
-		if (theMap.containsKey(key1)) {
-			if (theMap.get(key1).containsKey(key2)) {
-				double current = get(key1, key2);
-				put(key1, key2, current + value);
-			} else {
-				theMap.get(key1).put(key2, value);
-			}
-		} else {
-			put(key1, key2, value);
-		}
-	}
-}
diff --git a/src/ac/ed/lurg/utils/DualKey.java b/src/ac/ed/lurg/utils/DualKey.java
deleted file mode 100644
index 0841eaedc6dfedaf090e18b673c16f2100e5849a..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/utils/DualKey.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package ac.ed.lurg.utils;
-
-public class DualKey<K, Q> {
-	private K key1;
-	private Q key2;
-	
-	public DualKey(K key1, Q key2) {
-		this.key1 = key1;
-		this.key2 = key2;
-	}
-
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((key1 == null) ? 0 : key1.hashCode());
-		result = prime * result + ((key2 == null) ? 0 : key2.hashCode());
-		return result;
-	}
-
-	@SuppressWarnings("unchecked")
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (getClass() != obj.getClass())
-			return false;
-		DualKey<K, Q> other = (DualKey<K, Q>) obj;
-		if (key1 == null) {
-			if (other.key1 != null)
-				return false;
-		} else if (!key1.equals(other.key1))
-			return false;
-		if (key2 == null) {
-			if (other.key2 != null)
-				return false;
-		} else if (!key2.equals(other.key2))
-			return false;
-		return true;
-	}	
-}
diff --git a/src/ac/ed/lurg/utils/TripleMap.java b/src/ac/ed/lurg/utils/TripleMap.java
deleted file mode 100644
index aa9e32dd959b704fe0451ffd039390b1641aecce..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/utils/TripleMap.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package ac.ed.lurg.utils;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class TripleMap<K, L, M, V> {
-	
-	private Map<K, DoubleMap<L, M, V >> theMap = new HashMap<K, DoubleMap<L, M, V>>(); 
-	
-	public TripleMap() {
-		
-	}
-	
-	public void put(K key1, L key2, M key3, double value) {
-		if (theMap.containsKey(key1)) {
-			theMap.get(key1).put(key2, key3, value);	
-		} else {
-			DoubleMap<L, M, V> innerMap = new DoubleMap<L, M, V>();
-			innerMap.put(key2, key3, value);
-			theMap.put(key1, innerMap);
-		}
-	} 
-
-	public Double get(K key1, L key2, M key3) {
-		if (theMap.containsKey(key1)) {
-			return theMap.get(key1).get(key2, key3);
-		} else {
-			return null;
-		}
-	}
-	
-	public DoubleMap<L, M, V> getInnerMap(K key) {
-		return theMap.get(key);
-
-	}
-	
-	public Map<K, DoubleMap<L, M, V >> getMap() {
-		return theMap;
-	}
-}
diff --git a/src/ac/ed/lurg/yield/YieldClusterPoint.java b/src/ac/ed/lurg/yield/YieldClusterPoint.java
index 75943f0479f3bd33d36da3413b562fb459addc10..710e9ce7fcea4eb9690944ebf5b4d2abd44467a9 100644
--- a/src/ac/ed/lurg/yield/YieldClusterPoint.java
+++ b/src/ac/ed/lurg/yield/YieldClusterPoint.java
@@ -40,7 +40,7 @@ public class YieldClusterPoint implements ClusteringPoint<String> {
 		this.pasture = yields.getYield(YieldType.NO_FERT_NO_IRRIG, CropType.PASTURE);
 		this.wheatMax = yields.getYield(YieldType.FERT_MAX_IRRIG_MAX, CropType.WHEAT);
 		this.maizeMax = yields.getYield(YieldType.FERT_MAX_IRRIG_MAX, CropType.MAIZE);
-		this.irrigWaterAval = irrigItem.getIrrigConstraint();
+		this.irrigWaterAval = (irrigItem==null) ? 0.0 :irrigItem.getIrrigConstraint();
 		this.protectedArea = protectedArea;
 	}