diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms
index ad6ec0e95c4d8522f105bb4db6c13ed416d7c473..9da402d1201d464a84e4644c3b21d8fd6c0b4137 100644
--- a/GAMS/IntExtOpt.gms
+++ b/GAMS/IntExtOpt.gms
@@ -66,6 +66,7 @@
  SCALAR domesticPriceMarkup                  factor price increased from cost of production;
  SCALAR maxLandExpansionRate                 max rate of country land expansion;
 
+ SCALAR woodDemand;
  SCALAR carbonPrice                                 price of carbon - $1000 per tonne;
  SCALAR woodPrice                                   price of wood and timber - $1000 per tC-eq;
  SCALAR forestRotationPeriod;
@@ -81,7 +82,7 @@ $load yieldNone, yieldFertOnly, yieldIrrigOnly, yieldBoth, yieldShock
 $load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxNetImport, minNetImport, unhandledCropRate, setAsideRate, maxLandExpansionRate, subsidyRate
 $load meatEfficency, otherICost, irrigCost, irrigMaxRate, irrigConstraint, fertiliserUnitCost, domesticPriceMarkup, minDemandPerCereal, minDemandPerOilcrop, seedAndWasteRate
 $load previousLandCoverArea, carbonFluxRate, woodYieldLuc, carbonPrice, woodPrice
-$load conversionCost, woodYieldRota, infrastructureCost, forestRotationPeriod, woodMaxNetImport, woodMinNetImport, managedForestAreaMaxChangeRate
+$load conversionCost, woodYieldRota, infrastructureCost, forestRotationPeriod, woodMaxNetImport, woodMinNetImport, managedForestAreaMaxChangeRate, woodDemand
 $gdxin
 
  SCALAR delta "use to smooth power function see 7.5 www.gams.com/dd/docs/solversconopt.pdf" / 0.00000000001 /;
@@ -138,12 +139,14 @@ $gdxin
        totalConversionCost(location)      land cover conversion cost - $1000 per ha
        woodHarvest(location)              wood harvested from timber forest rotation - Mt C-eq
        woodExported                       total wood sold - Mt C-eq
+       woodImported
+       woodNetSupply
        carbonFlux(location)               total carbon flux  - Mt C
 *       A                                  "artificial variable for debugging https://www.gams.com/blog/2017/07/misbehaving-model-infeasible/"
        total_cost                         total cost of domestic supply including net imports;
 
  POSITIVE VARIABLE cropArea, fertI, irrigI, otherIntensity, ruminantFeed, monogastricFeed, importAmount, exportAmount,
-                   totalFeedDM, landCoverArea, landCoverChange, totalConversionCost, woodHarvest, woodExported;
+                   totalFeedDM, landCoverArea, landCoverChange, totalConversionCost, woodHarvest, woodExported, woodImported;
 
 * POSITIVE VARIABLE A;
 
@@ -173,10 +176,12 @@ $gdxin
        LAND_COVER_CHANGE_CALC(land_cover, location)     calc land cover change
        LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) conservation of land area
        CONVERSION_COST(location)                          cost of land cover conversion
-       MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest)
-       MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest)
+*       MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest)
+*       MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest)
 
        WOOD_HARVEST_CALC(location)                   calc wood harvested from timber forest rotation
+       WOOD_NET_SUPPLY_CALC
+       WOOD_DEMAND_CONSTRAINT
        WOOD_EXPORT_CONSTRAINT                         constraint on maximum wood export
        WOOD_MIN_TRADE_CONSTRAINT                          constraint on minimum wood export
        WOOD_MAX_TRADE_CONSTRAINT
@@ -240,22 +245,22 @@ $gdxin
 
  CONVERSION_COST(location) .. totalConversionCost(location) =E= sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * conversionCost(land_cover_before, land_cover_after));
 
-* MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) .. sum(exc_managed_forest, landCoverChange(exc_managed_forest, managed_forest, location)) =L= sum(exc_managed_forest, previousLandCoverArea(exc_managed_forest, location)) * managedForestAreaMaxChangeRate;
-
-* MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) .. landCoverArea(managed_forest, location) =G= previousLandCoverArea(managed_forest, location) * (1 - managedForestAreaMaxChangeRate);
- 
- MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =L= sum(location, previousLandCoverArea(managed_forest, location)) * (1 + managedForestAreaMaxChangeRate);
-
- MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =G= sum(location, previousLandCoverArea(managed_forest, location)) * (1 - managedForestAreaMaxChangeRate);
+* MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =L= sum(location, previousLandCoverArea(managed_forest, location)) * (1 + managedForestAreaMaxChangeRate);
+* MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =G= sum(location, previousLandCoverArea(managed_forest, location)) * (1 - managedForestAreaMaxChangeRate);
 
 * Wood from timberForest rotation and new timberForest
- WOOD_HARVEST_CALC(location) .. woodHarvest(location) =E= sum(land_cover, landCoverChange(land_cover, 'timberForest', location) * woodYieldRota(land_cover, 'timberForest', location) / forestRotationPeriod);
+ WOOD_HARVEST_CALC(location) .. woodHarvest(location) =E= sum(land_cover, landCoverChange(land_cover, 'timberForest', location) * woodYieldRota(land_cover, 'timberForest', location) / forestRotationPeriod) +
+                                                          sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * woodYieldLuc(land_cover_before, land_cover_after, location));
+
+ WOOD_NET_SUPPLY_CALC .. woodNetSupply =E= sum(location, woodHarvest(location)) - woodExported + woodImported;
+ 
+ WOOD_DEMAND_CONSTRAINT .. woodNetSupply =G= woodDemand;
 
- WOOD_EXPORT_CONSTRAINT .. woodExported =E= sum(location, woodHarvest(location));
+ WOOD_EXPORT_CONSTRAINT .. woodExported =L= sum(location, woodHarvest(location));
 
- WOOD_MIN_TRADE_CONSTRAINT .. 0 - woodExported =L= woodMaxNetImport;
+ WOOD_MIN_TRADE_CONSTRAINT .. woodImported - woodExported =L= woodMaxNetImport;
 
- WOOD_MAX_TRADE_CONSTRAINT .. 0 - woodExported =G= woodMinNetImport;
+ WOOD_MAX_TRADE_CONSTRAINT .. woodImported - woodExported =G= woodMinNetImport;
 
  CARBON_FLUX_CALC(location) .. carbonFlux(location) =E= sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * carbonFluxRate(land_cover_before, land_cover_after, location));
 
@@ -268,7 +273,7 @@ $gdxin
 
                SUM(location, totalConversionCost(location)) +
 
-               [-woodPrice * woodExported] +
+               [woodPrice * (woodImported - woodExported)] +
 
                SUM(location, carbonFlux(location)) * carbonPrice
          );
@@ -286,7 +291,6 @@ $gdxin
  exportAmount.L(all_types) = previousExportAmount(all_types);
  totalConversionCost.L(location) = sum(land_cover, landCoverChange.L(land_cover, land_cover, location) * conversionCost(land_cover, land_cover));
  woodHarvest.L(location) = previousLandCoverArea('timberForest', location) * woodYieldRota('timberForest', 'timberForest', location) / forestRotationPeriod;
- woodExported.L = sum(location, woodHarvest.L(location));
 
 
 * LAND_USE.OptFile = 1;
@@ -308,6 +312,8 @@ $gdxin
  parameter feedCostRate(feed_crop);
  parameter productionShock(all_types);
  scalar netCarbonFlux;
+ scalar netWoodImport;
+ scalar totalWoodHarvest;
 
 * Production quantities based on smaller area (before unhandledCropArea adjustment applied)
  totalProd(crop) = sum(location, cropArea.l(crop, location) * yield.l(crop, location));
@@ -329,6 +335,8 @@ $gdxin
  netImportAmount(import_crop) = importAmount.l(import_crop) - exportAmount.l(import_crop);
 
  netCarbonFlux = SUM(location, carbonFlux.L(location));
+ netWoodImport = woodImported.L - woodExported.L;
+ totalWoodHarvest = sum(location, woodHarvest.L(location));
 
  Scalar totalCostsLessLU;
 
diff --git a/debug_config.properties b/debug_config.properties
index 92d926b6c77a203f249e9d7d14fad51f886ea83c..1432cdd4b78a712a13d35ca2707c5ba6f0039848 100644
--- a/debug_config.properties
+++ b/debug_config.properties
@@ -10,7 +10,7 @@ TIMESTEP_SIZE=1
 END_TIMESTEP=90
 
 IS_CALIBRATION_RUN=true
-END_FIRST_STAGE_CALIBRATION=10
+END_FIRST_STAGE_CALIBRATION=2
 
 GENERATE_NEW_YIELD_CLUSTERS=false
 
@@ -20,5 +20,12 @@ DEBUG_LIMIT_COUNTRIES=true
 DEBUG_COUNTRY_NAME=Brazil
 GAMS_COUNTRY_TO_SAVE=Brazil
 
-WOOD_PRICE=0.1
-CARBON_PRICE=0
\ No newline at end of file
+INIT_WOOD_PRICE=0.2
+INIT_WOOD_STOCK=1000
+INIT_CARBON_PRICE=0.0
+FOREST_ROTATION_PERIOD=30
+INFRASTRUCTURE_EXPANSION_COST=0.0
+MANAGED_FOREST_INCREASE_COST=0.03
+MANAGED_FOREST_DECREASE_COST=0.03
+FOREST_MANAGEMENT_COST=0.05
+FOREST_MAX_CHANGE=0.02
\ No newline at end of file
diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java
index 2a749832361ab224a8184fb3bfcfe52e9b4e1ff8..bd5735fae189417f01da2323e43dfaad28e017e9 100644
--- a/src/ac/ed/lurg/InternationalMarket.java
+++ b/src/ac/ed/lurg/InternationalMarket.java
@@ -71,7 +71,7 @@ public class InternationalMarket {
 		return initialStockLevels;
 	}
 
-	void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double gen2EcDDemand, double carbonDemand, double timberDemand, Timestep timestep) {
+	void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double gen2EcDDemand, double carbonDemand, Timestep timestep) {
 		CropToDoubleMap totalImportCommodities = new CropToDoubleMap();
 		CropToDoubleMap totalExportCommodities = new CropToDoubleMap();
 		CropToDoubleMap totalProduction = new CropToDoubleMap();
@@ -133,17 +133,25 @@ public class InternationalMarket {
 		carbonPrice = adjustedCPrice;
 		
 		// Update timber price
-		double totalTimberProduced = 0;
+		double totalWoodImport = 0;
+		double totalWoodExport = 0;
+		double totalWoodProduction = 0;
 		for (AbstractCountryAgent ca : countryAgents) {
-			totalTimberProduced += ca.getTimberProduction();
+			totalWoodProduction += ca.getWoodUsageData().getHarvest();
+			double netImport = ca.getWoodUsageData().getNetImport();
+			if (netImport >= 0) {
+				totalWoodImport += netImport;
+			} else {
+				totalWoodExport += -netImport;
+			}
 		}
-		totalTimberProduced = Math.max(totalTimberProduced, 0.0000001); // avoid division by 0
+		totalWoodProduction = Math.max(totalWoodProduction, 0.0000001); // avoid division by 0
 		GlobalPrice prevTPrice = woodPrice;
-		LogWriter.println(timestep.getYear() + " Updating timber price");
-		GlobalPrice adjustedTPrice = prevTPrice.createWithUpdatedMarketPrices(timberDemand, totalTimberProduced, timestep, totalTimberProduced, false);
-		LogWriter.println( String.format("Price for timber updated from %s to %s \n", prevTPrice, adjustedTPrice));
+		LogWriter.println(timestep.getYear() + " Updating wood price");
+		GlobalPrice adjustedTPrice = prevTPrice.createWithUpdatedMarketPrices(totalWoodImport, totalWoodExport, timestep, totalWoodProduction, true);
+		LogWriter.println( String.format("Price for wood updated from %s to %s \n", prevTPrice, adjustedTPrice));
 		if (adjustedTPrice.getStockLevel() < 0)
-			LogWriter.println("Global stocks are below zero timber, " + timestep.getYear());
+			LogWriter.println("Global stocks are below zero wood, " + timestep.getYear());
 		woodPrice = adjustedTPrice;
 	}
 
diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
index 3ba6b590328f1bdd7e9fe81fc81d9b9ba3e3ffe9..f1d0c3031e3818a984546c5729f688fa1edfe313 100755
--- a/src/ac/ed/lurg/ModelConfig.java
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -437,5 +437,7 @@ public class ModelConfig {
 	public static final double MANAGED_FOREST_INCREASE_COST = getDoubleProperty("MANAGED_FOREST_INCREASE_COST", 0.5 * LAND_CHANGE_COST); // $1000/ha
 	public static final double MANAGED_FOREST_DECREASE_COST = getDoubleProperty("MANAGED_FOREST_DECREASE_COST", 0.3 * LAND_CHANGE_COST); // $1000/ha
 	public static final double INFRASTRUCTURE_EXPANSION_COST = getDoubleProperty("INFRASTRUCTURE_EXPANSION_COST", 0.2); // $1000/ha
-	public static final double FOREST_MAX_CHANGE = getDoubleProperty("FOREST_MAX_CHANGE", 0.02); // $1000/ha
+	public static final double FOREST_MAX_CHANGE = getDoubleProperty("FOREST_MAX_CHANGE", 0.02);
+	public static final double ROUNDWOOD_DEMAND_ELASTICITY = getDoubleProperty("ROUNDWOOD_DEMAND_ELASTICITY", 0.5);
+	public static final double FUELWOOD_DEMAND_ELASTICITY = getDoubleProperty("FUELDWOOD_DEMAND_ELASTICITY", -0.5);
 }
diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java
index 0871123917c4a93dd83df55c43daf474e852b0c7..357c675b64f4841486f5938556f2e25fd37ce19f 100644
--- a/src/ac/ed/lurg/ModelMain.java
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -147,25 +147,21 @@ public class ModelMain {
 		double carbonDemand = demandManager.getCarbonDemand(ModelConfig.IS_CALIBRATION_RUN ? new Timestep(1) : timestep);
 		double carbonDemandIncrease = (carbonDemand > previousCarbonDemand) ? carbonDemand - previousCarbonDemand: 0;
 		
-		double previousTimberDemand = (timestep.isInitialTimestep() || ModelConfig.IS_CALIBRATION_RUN ) ? 0: demandManager.getTimberDemand(timestep.getPreviousTimestep());
-		double timberDemand = demandManager.getTimberDemand(ModelConfig.IS_CALIBRATION_RUN ? new Timestep(1) : timestep);
-		double timberDemandIncrease = (timberDemand > previousTimberDemand) ? timberDemand - previousTimberDemand: 0;
-		
 		CarbonFluxRasterSet currentCarbonFluxData = getCarbonFluxData(timestep);
 		WoodYieldRasterSet currentWoodYieldData = getWoodYieldData(timestep);
 		
 		DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts = new ConversionCostReader().read();
 		
 		countryAgents.determineProductionForAll(timestep, yieldSurfaces, currentIrrigationData, gen2Increase, currentCarbonFluxData, 
-				currentWoodYieldData, conversionCosts, carbonDemandIncrease, timberDemandIncrease);
-		internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timberDemand, timestep);
+				currentWoodYieldData, conversionCosts, carbonDemandIncrease);
+		internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timestep);
 		
 		// loop for iterations.  Could check within a tolerance using internationalMarket.findMaxPriceDiff, not doing so as doesn't find a solution due to inelastic consumption
 		for (int i=0; i < ModelConfig.DEMAND_RECALC_MAX_ITERATIONS; i++) {
 			LogWriter.println("\n++ Re-estimating prices and demand " + i);
 			countryAgents.recalculateDemandForAll(); // recalculate demand from new prices
 			countryAgents.updateNetImportsForAll(); // calculate imports and exports
-			internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timberDemand, timestep); // calculate prices
+			internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timestep); // calculate prices
 		}
 		internationalMarket.applyPriceShocks(timestep);
 		
@@ -574,7 +570,7 @@ public class ModelMain {
 		Map<CompositeCountry, Double[]> initWoodAndCarbonProd;
 		if (ModelConfig.IS_CALIBRATION_RUN) {
 			initWoodAndCarbonProd = new HashMap<CompositeCountry, Double[]>();
-			Double[] initProdValues = {0.0, 0.0};
+			Double[] initProdValues = {0.0, 0.0}; // TODO Read in
 			for (CompositeCountry cc : compositeCountryManager.getAll()) {
 				initWoodAndCarbonProd.put(cc, initProdValues);
 			}
diff --git a/src/ac/ed/lurg/country/AbstractCountryAgent.java b/src/ac/ed/lurg/country/AbstractCountryAgent.java
index 8aa5d9c87cc0256fe372206449cb3ce96b08bf14..58d7adf9be0182676998634ca4bc135db80d7243 100644
--- a/src/ac/ed/lurg/country/AbstractCountryAgent.java
+++ b/src/ac/ed/lurg/country/AbstractCountryAgent.java
@@ -7,8 +7,10 @@ import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.Timestep;
 import ac.ed.lurg.demand.AbstractDemandManager;
 import ac.ed.lurg.landuse.CropUsageData;
+import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.types.WoodType;
 import ac.ed.lurg.utils.LogWriter;
 
 public abstract class AbstractCountryAgent {
@@ -23,6 +25,7 @@ public abstract class AbstractCountryAgent {
 	protected Map<CommodityType, Map<CropType, Double>> baseDemandFact;
 	protected Timestep currentTimestep;
 	protected Map<CommodityType, Map<CropType, Double>> currentMinDemandFract;
+	protected Map<WoodType, Double> currentWoodDemand;
 	
 	public AbstractCountryAgent(AbstractDemandManager demandManager,CompositeCountry country, Map<CropType, Double> tradeBarriers) {
 
@@ -60,6 +63,7 @@ public abstract class AbstractCountryAgent {
 	private void calculateDemand(boolean outputGamsDemand) {
 		Map<CommodityType, Double> producerPrices = getProducerCommodityPrices();
 		currentProjectedDemand = demandManager.getDemand(country, currentTimestep.getYear(), producerPrices, outputGamsDemand);
+		currentWoodDemand = demandManager.getWoodDemandComposite(country, currentTimestep.getYear());
 	}
 	
 	protected void calculateCountryPricesAndDemand(Map<CropType, GlobalPrice> worldPrices, GlobalPrice carbonPrice, GlobalPrice timberPrice, boolean outputGamsDemand) {
@@ -176,6 +180,6 @@ public abstract class AbstractCountryAgent {
 	
 	abstract public double getNetCarbonFlux();
 	
-	abstract public double getTimberProduction();
+	abstract public WoodUsageData getWoodUsageData();
 	
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
index 1289c2206d267c23017a8a7f76df78f2b19a10d4..184e433c2b8db64071c7847fdcd2c507b4f4e764 100644
--- a/src/ac/ed/lurg/country/CountryAgent.java
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -20,6 +20,7 @@ import ac.ed.lurg.landuse.CarbonFluxItem;
 import ac.ed.lurg.landuse.CropUsageData;
 import ac.ed.lurg.landuse.IrrigationItem;
 import ac.ed.lurg.landuse.LandUseItem;
+import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.landuse.WoodYieldItem;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
@@ -106,7 +107,7 @@ public class CountryAgent extends AbstractCountryAgent {
 	public GamsRasterOutput determineProduction(YieldRaster countryYieldSurfaces, RasterSet<IrrigationItem> irrigData, 
 			Map<CropType, GlobalPrice> worldPrices, double globalGen2EcIncrease, RasterSet<CarbonFluxItem> carbonFluxData, 
 			RasterSet<WoodYieldItem> woodYieldData, DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts,
-			double carbonDemandIncrease, GlobalPrice carbonPrice, double timberDemandIncrease, GlobalPrice timberPrice) {
+			double carbonDemandIncrease, GlobalPrice carbonPrice, GlobalPrice timberPrice) {
 
 		// project demand
 		calculateCountryPricesAndDemand(worldPrices, carbonPrice, timberPrice, false);
@@ -129,7 +130,7 @@ public class CountryAgent extends AbstractCountryAgent {
 			
 			// optimize areas and intensity 
 			GamsRasterInput input = getGamsRasterInput(irrigData, countryYieldSurfaces, globalGen2EcIncrease, carbonFluxData, woodYieldData, conversionCosts, 
-					carbonDemandIncrease, timberDemandIncrease);
+					carbonDemandIncrease);
 			GamsRasterOptimiser opti = new GamsRasterOptimiser(input, yieldClusters);
 			LogWriter.println("Running " + country.getName() + ", currentTimestep " + currentTimestep);
 
@@ -168,7 +169,7 @@ public class CountryAgent extends AbstractCountryAgent {
 
 	private GamsRasterInput getGamsRasterInput(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces, double gen2EcIncrease,
 			RasterSet<CarbonFluxItem> carbonFluxData, RasterSet<WoodYieldItem> woodYieldData, DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts,
-			double carbonDemandIncrease, double timberDemandIncrease) {
+			double carbonDemandIncrease) {
 		double allowedImportChange;
 
 		if (currentTimestep.isInitialTimestep() || (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION)) {  // initialisation time-step
@@ -226,30 +227,22 @@ public class CountryAgent extends AbstractCountryAgent {
 		// Timber import/export constraints
 		TradeConstraint timberTradeConstraint;
 		{
-			double baseTrade = -getTimberProduction(); // net imports negative as all exported
-			double countryArea = LandUseItem.getTotalLandArea(previousGamsRasterOutput.getLandUses().values());
+			double baseTrade;
+			if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.isInitialTimestep()) {
+				baseTrade = -1;
+			} else {
+				baseTrade = getWoodUsageData().getNetImport();
+			}
 			double changeUp = 0.0;
 			double changeDown = 0.0;
 			if (allowedImportChange > 0.0) {
-				changeDown = Math.min(baseTrade * allowedImportChange, -(timberDemandIncrease * countryArea / 15000 * 2));
-				changeUp = baseTrade * allowedImportChange;
+				changeUp = changeDown = baseTrade * allowedImportChange;
 			}
-			// Initial timber production for calibration run
-			if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.isInitialTimestep()) {
-				double forestedAndNaturalArea = LandUseItem.getTotalLandCover(previousGamsRasterOutput.getLandUses().values(), LandCoverType.NATURAL) +
-						LandUseItem.getTotalLandCover(previousGamsRasterOutput.getLandUses().values(), LandCoverType.TIMBER_FOREST);
-				changeDown = -timberDemandIncrease * forestedAndNaturalArea / 4000;
-				if (ModelConfig.DEBUG_LIMIT_COUNTRIES) {changeDown = -timberDemandIncrease;} // for debugging purposes only
-			} else {
-				changeDown = Math.min(baseTrade * allowedImportChange, -(timberDemandIncrease * countryArea / 15000 * 2)); // allow export in case baseTrade is 0
-			}
-			changeUp = baseTrade * allowedImportChange;
-
 			timberTradeConstraint = new TradeConstraint(baseTrade + changeDown, baseTrade - changeUp);
 		}
 
 		GamsCountryInput countryLevelInputs = new GamsCountryInput(country, currentProjectedDemand, currentCountryPrices, importConstraints, 
-				previousGamsRasterOutput.getCropUsageData(), currentMinDemandFract, subsidyRates, currentCarbonPrice, carbonTradeConstraint, currentTimberPrice, timberTradeConstraint);	
+				previousGamsRasterOutput.getCropUsageData(), currentMinDemandFract, subsidyRates, currentCarbonPrice, carbonTradeConstraint, currentWoodDemand, currentTimberPrice, timberTradeConstraint);	
 		GamsRasterInput input = new GamsRasterInput(currentTimestep, countryYieldSurfaces, previousGamsRasterOutput.getLandUses(), irrigData, 
 				carbonFluxData, woodYieldData, conversionCosts, countryLevelInputs);
 
@@ -343,7 +336,7 @@ public class CountryAgent extends AbstractCountryAgent {
 		return previousGamsRasterOutput.getNetCarbonFlux();
 	}
 	
-	public double getTimberProduction() {
-		return previousGamsRasterOutput.getTimberProduction();
+	public WoodUsageData getWoodUsageData() {
+		return previousGamsRasterOutput.getWoodUsageData();
 	}
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/CountryAgentManager.java b/src/ac/ed/lurg/country/CountryAgentManager.java
index 0111e52c1703957cd421e015357832dd9e0e6f61..56d07768a0a823926876dfa19c0fb1f9dd5744b4 100644
--- a/src/ac/ed/lurg/country/CountryAgentManager.java
+++ b/src/ac/ed/lurg/country/CountryAgentManager.java
@@ -106,7 +106,7 @@ public class CountryAgentManager {
 
 	public void determineProductionForAll(Timestep timestep, YieldRaster yieldSurfaces, IrrigationRasterSet currentIrrigationData, double gen2Increase,
 			CarbonFluxRasterSet currentCarbonFluxData, WoodYieldRasterSet currentWoodYieldData, DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts,
-			double carbonDemandIncrease, double timberDemandIncrease) {
+			double carbonDemandIncrease) {
 		
 		for (AbstractCountryAgent aca : countryAgents) {		
 			aca.setCurrentTimestep(timestep);
@@ -122,7 +122,7 @@ public class CountryAgentManager {
 			
 			try {
 				ca.determineProduction(countryYieldSurfaces, irrigData, internationalMarket.getWorldPrices(), gen2Increase, carbonFluxData, woodYieldData, 
-						conversionCosts, carbonDemandIncrease, internationalMarket.getCarbonPrice(), timberDemandIncrease, internationalMarket.getWoodPrice());
+						conversionCosts, carbonDemandIncrease, internationalMarket.getCarbonPrice(), internationalMarket.getWoodPrice());
 				
 				// update global rasters
 				globalLandUseRaster.putAll(ca.getLandUses());
@@ -187,7 +187,7 @@ public class CountryAgentManager {
 	public void serializeTimberAndCarbonProdForAll() {
 		Map<CompositeCountry, Double[]> timberAndCarbonProdMap = new HashMap<CompositeCountry, Double[]>();
 		for (CountryAgent ca : gamsCountryAgents) {
-			Double[] timberAndCarbonProd = {ca.getTimberProduction(), ca.getNetCarbonFlux()};
+			Double[] timberAndCarbonProd = {ca.getWoodUsageData().getNetImport(), ca.getNetCarbonFlux()};
 			timberAndCarbonProdMap.put(ca.country, timberAndCarbonProd);
 		}
 		
diff --git a/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
index dc5d375319f53186bbc92df4b924cb0738090200..2a14d09916880a40110f963b8048f3cdc944ad0b 100644
--- a/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
+++ b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
@@ -9,6 +9,7 @@ import ac.ed.lurg.country.CompositeCountry;
 import ac.ed.lurg.country.GlobalPrice;
 import ac.ed.lurg.demand.AbstractDemandManager;
 import ac.ed.lurg.landuse.CropUsageData;
+import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
 
@@ -54,8 +55,10 @@ public class CraftyCountryAgent extends AbstractCountryAgent {
 	public double getNetCarbonFlux() {
 		return 0;
 	}
-	
-	public double getTimberProduction() {
-		return 0;
+
+	@Override
+	public WoodUsageData getWoodUsageData() {
+		// TODO Auto-generated method stub
+		return null;
 	}
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/gams/GamsCountryInput.java b/src/ac/ed/lurg/country/gams/GamsCountryInput.java
index f8298e5b05467ec984da49cb81d3add0f897bdd0..252b1bbc0d61dbd224a76f1bb43351bc7e788722 100644
--- a/src/ac/ed/lurg/country/gams/GamsCountryInput.java
+++ b/src/ac/ed/lurg/country/gams/GamsCountryInput.java
@@ -10,6 +10,7 @@ import ac.ed.lurg.country.TradeConstraint;
 import ac.ed.lurg.landuse.CropUsageData;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.types.WoodType;
 
 public class GamsCountryInput {
 
@@ -22,13 +23,15 @@ public class GamsCountryInput {
 	private Map<CropType, Double> subsidyRates;
 	private CountryPrice carbonPrice;
 	private TradeConstraint carbonTradeConstraint;
+	private Map<WoodType, Double> woodDemand;
 	private CountryPrice woodPrice;
 	private TradeConstraint timberTradeConstraint;
 
 	public GamsCountryInput(CompositeCountry country, Map<CommodityType, Double> projectedDemand, Map<CropType, CountryPrice> countryPrices, 
 			Map<CropType, TradeConstraint> importConstraints, Map<CropType, CropUsageData> previousCropUsageData, 
 			Map<CommodityType, Map<CropType, Double>> minDemandFracts, Map<CropType, Double> subsidyRates,
-			CountryPrice carbonPrice, TradeConstraint carbonTradeConstraint, CountryPrice woodPrice, TradeConstraint timberTradeConstraint) {
+			CountryPrice carbonPrice, TradeConstraint carbonTradeConstraint, Map<WoodType, Double> woodDemand, 
+			CountryPrice woodPrice, TradeConstraint timberTradeConstraint) {
 		super();
 		this.country = country;
 		this.projectedDemand = projectedDemand;
@@ -39,6 +42,7 @@ public class GamsCountryInput {
 		this.subsidyRates = subsidyRates;
 		this.carbonPrice = carbonPrice;
 		this.carbonTradeConstraint = carbonTradeConstraint;
+		this.woodDemand = woodDemand;
 		this.woodPrice = woodPrice;
 		this.timberTradeConstraint = timberTradeConstraint;
 	}
@@ -101,6 +105,10 @@ public class GamsCountryInput {
 		return carbonTradeConstraint;
 	}
 
+	public Map<WoodType, Double> getWoodDemand() {
+		return woodDemand;
+	}
+
 	public CountryPrice getWoodPrice() {
 		return woodPrice;
 	}
diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
index 3a55910ef04609097e6526376d2be5f032fffac6..211385243c1735e39aa1ac2644d46fd5305ba082 100644
--- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
@@ -30,11 +30,13 @@ import ac.ed.lurg.landuse.CropUsageData;
 import ac.ed.lurg.landuse.Intensity;
 import ac.ed.lurg.landuse.IrrigationItem;
 import ac.ed.lurg.landuse.LandUseItem;
+import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.landuse.WoodYieldItem;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.types.LandCoverType;
 import ac.ed.lurg.types.Parameter;
+import ac.ed.lurg.types.WoodType;
 import ac.ed.lurg.types.YieldType;
 import ac.ed.lurg.utils.DoubleMap;
 import ac.ed.lurg.utils.LazyHashMap;
@@ -342,6 +344,12 @@ public class GamsLocationOptimiser {
 		addScalar(inDB, "woodMaxNetImport", countryInput.getWoodTradeConstraint().getMaxConstraint(), 5);
 		addScalar(inDB, "woodMinNetImport", countryInput.getWoodTradeConstraint().getMinConstraint(), 5);
 		
+		// Wood demand
+		Map<WoodType, Double> woodDemandMap = countryInput.getWoodDemand();
+		double totalWoodDemand = woodDemandMap.values().stream().reduce(0.0, Double::sum);
+		addScalar(inDB, "woodDemand", totalWoodDemand, 5);
+		
+		
 		// Carbon fluxes
 		GAMSParameter cFluxRateP = inDB.addParameter("carbonFluxRate", 3);
 		
@@ -604,9 +612,11 @@ public class GamsLocationOptimiser {
 		double netCarbonFlux = outDB.getParameter("netCarbonFlux").getFirstRecord().getValue();
 		
 		// Timber harvest
-		double totalTimberProduction = outDB.getVariable("woodExported").getFirstRecord().getLevel();
+		double totalTimberProduction = outDB.getParameter("totalWoodHarvest").getFirstRecord().getValue();
+		double netWoodImport = outDB.getParameter("netWoodImport").getFirstRecord().getValue();
+		WoodUsageData woodUsageData = new WoodUsageData(totalTimberProduction, netWoodImport);
 
-		GamsLocationOutput results = new GamsLocationOutput(modelStatus, landUses, cropUsageData, landCoverChanges, gamsLandCoverChanges, netCarbonFlux, totalTimberProduction);
+		GamsLocationOutput results = new GamsLocationOutput(modelStatus, landUses, cropUsageData, landCoverChanges, gamsLandCoverChanges, netCarbonFlux, woodUsageData);
 		return results;
 	}
 	
diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOutput.java b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java
index f475de6d4e90f446ad11c28bfd85f5f54ab88d56..0876507f0325a3868d6cede31eaae06fb62d3704 100644
--- a/src/ac/ed/lurg/country/gams/GamsLocationOutput.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java
@@ -7,6 +7,7 @@ import com.gams.api.GAMSGlobals.ModelStat;
 
 import ac.ed.lurg.landuse.CropUsageData;
 import ac.ed.lurg.landuse.LandUseItem;
+import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.utils.TripleMap;
 import ac.ed.lurg.types.LandCoverType;
@@ -20,14 +21,14 @@ public class GamsLocationOutput {
 	private TripleMap<Integer, LandCoverType, LandCoverType, Double> landCoverChanges;
 	ArrayList<LandCoverChangeItem> gamsLandCoverChanges;
 	private double netCarbonFlux;
-	private double timberProduction;
+	private WoodUsageData woodUsageData;
 	
 	public GamsLocationOutput(ModelStat status, 
 			Map<Integer, LandUseItem> landUses, 
 			Map<CropType, CropUsageData> cropUsageData,
 			TripleMap<Integer, LandCoverType, LandCoverType, Double> landCoverChange,
 			ArrayList<LandCoverChangeItem> gamsLandCoverChanges,
-			double netCarbonFlux, double timberProduction) {
+			double netCarbonFlux, WoodUsageData woodUsageData) {
 		super();
 		this.status = status;
 		this.landUses = landUses;
@@ -35,7 +36,7 @@ public class GamsLocationOutput {
 		this.landCoverChanges = landCoverChange;
 		this.gamsLandCoverChanges = gamsLandCoverChanges;
 		this.netCarbonFlux = netCarbonFlux;
-		this.timberProduction = timberProduction;
+		this.woodUsageData = woodUsageData;
 	}
 	
 	public ModelStat getStatus() {
@@ -60,8 +61,8 @@ public class GamsLocationOutput {
 	public double getNetCarbonFlux() {
 		return netCarbonFlux;
 	}
-	
-	public double getTimberProduction() {
-		return timberProduction;
+
+	public WoodUsageData getWoodUsageData() {
+		return woodUsageData;
 	}
 }
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
index 049d10a1b9be86ed34d2ca5d898d82c7b0eae730..a724510f0779a3e155ee49f4d72e4aa0f6a94beb 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
@@ -59,7 +59,7 @@ public class GamsRasterOptimiser {
 		}
 
 		return new GamsRasterOutput(gamsOutput.getStatus(), newIntensityRaster, gamsOutput.getCommoditiesData(), gamsOutput.getGamsLandCoverChanges(), 
-				baseTimberYield, gamsOutput.getNetCarbonFlux(), gamsOutput.getTimberProduction());
+				baseTimberYield, gamsOutput.getNetCarbonFlux(), gamsOutput.getWoodUsageData());
 	}
 
 	private RasterSet<LandUseItem> createWithSameLandCovers(RasterSet<LandUseItem> toCopy) {
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOutput.java b/src/ac/ed/lurg/country/gams/GamsRasterOutput.java
index 24af77bc2c05a13768384d4da499a654e7520a4b..cade1516dc2e41e9fab56c3634f3b88145617bbf 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterOutput.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterOutput.java
@@ -8,6 +8,7 @@ import com.gams.api.GAMSGlobals.ModelStat;
 import ac.ed.lurg.country.LandCoverChangeItem;
 import ac.ed.lurg.landuse.CropUsageData;
 import ac.ed.lurg.landuse.LandUseItem;
+import ac.ed.lurg.landuse.WoodUsageData;
 import ac.ed.lurg.types.CropType;
 import ac.sac.raster.RasterSet;
 
@@ -18,8 +19,8 @@ public class GamsRasterOutput {
 	private Map<CropType, CropUsageData> cropUsageData;
 	private ArrayList<LandCoverChangeItem> gamsLandCoverChanges;
 	private Map<Integer, Double> baseTimberYield;
-	double netCarbonFlux;
-	double woodProduction;
+	private double netCarbonFlux;
+	private WoodUsageData woodUsageData;
 	
 	public GamsRasterOutput(RasterSet<LandUseItem> landUses, Map<CropType, CropUsageData> cropUsageData) {
 		super();
@@ -29,18 +30,18 @@ public class GamsRasterOutput {
 
 	public GamsRasterOutput(RasterSet<LandUseItem> landUses, Map<CropType, CropUsageData> cropUsageData, Double[] woodAndCarbonProd) {
 		this(landUses, cropUsageData);
-        this.woodProduction = woodAndCarbonProd[0];
+        this.woodUsageData = new WoodUsageData(woodAndCarbonProd[0], 0.0);
         this.netCarbonFlux = woodAndCarbonProd[1];
 	}
 
 	public GamsRasterOutput(ModelStat status, RasterSet<LandUseItem> intensityRaster, Map<CropType, CropUsageData> cropUsageData,
-			ArrayList<LandCoverChangeItem> gamsLandCoverChanges, Map<Integer, Double> baseTimberYield, double netCarbonFlux, double timberProduction) {
+			ArrayList<LandCoverChangeItem> gamsLandCoverChanges, Map<Integer, Double> baseTimberYield, double netCarbonFlux, WoodUsageData woodUsageData) {
 		this(intensityRaster, cropUsageData);
 		this.status = status;
 		this.gamsLandCoverChanges = gamsLandCoverChanges;
 		this.baseTimberYield = baseTimberYield;
 		this.netCarbonFlux = netCarbonFlux;
-		this.woodProduction = timberProduction;
+		this.woodUsageData = woodUsageData;
 	}
 	
 	public ModelStat getStatus() {
@@ -67,8 +68,8 @@ public class GamsRasterOutput {
 		return netCarbonFlux;
 	}
 
-	public double getTimberProduction() {
-		return woodProduction;
+	public WoodUsageData getWoodUsageData() {
+		return woodUsageData;
 	}
 	
 }
diff --git a/src/ac/ed/lurg/demand/AbstractDemandManager.java b/src/ac/ed/lurg/demand/AbstractDemandManager.java
index e981c1fedbe9dd8ebed766ef5cb355b233deb13d..99f8206a5272024eff35083c73e944f16c6f7651 100644
--- a/src/ac/ed/lurg/demand/AbstractDemandManager.java
+++ b/src/ac/ed/lurg/demand/AbstractDemandManager.java
@@ -11,6 +11,7 @@ import ac.ed.lurg.country.SingleCountry;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.types.Parameter;
+import ac.ed.lurg.types.WoodType;
 import ac.ed.lurg.utils.LogWriter;
 
 public abstract class AbstractDemandManager {
@@ -20,7 +21,7 @@ public abstract class AbstractDemandManager {
 	protected BioenergyDemandManager bioenergyDemandManager;
 	protected CerealFractionsManager cerealFractionsManager;
 	protected CarbonDemandManager carbonDemandManager;
-	protected TimberDemandManager timberDemandManager;
+	protected WoodDemandManager woodDemandManager;
 
 	public AbstractDemandManager(CompositeCountryManager compositeCountryManager, CalorieManager calorieManager) {
 		this.compositeCountryManager = compositeCountryManager;
@@ -28,7 +29,7 @@ public abstract class AbstractDemandManager {
 		bioenergyDemandManager = new BioenergyDemandManager();
 		cerealFractionsManager = new CerealFractionsManager(compositeCountryManager);
 		carbonDemandManager = new CarbonDemandManager();
-		timberDemandManager = new TimberDemandManager();
+		woodDemandManager = new WoodDemandManager();
 	}
 
 	public Map<CommodityType, Double> getDemand(CompositeCountry cc, int year, Map<CommodityType, Double> prices, boolean outputGamsDemand) {
@@ -139,8 +140,25 @@ public abstract class AbstractDemandManager {
 		return carbonDemandManager.getGlobalCarbonDemand(timestep.getYear());
 	}
 	
-	public double getTimberDemand(Timestep timestep) {
-		return timberDemandManager.getGlobalTimberDemand(timestep.getYear());
-	}
+	protected abstract Map<WoodType, Double> getWoodDemand(SingleCountry country, int year);
+	
+	public Map<WoodType, Double> getWoodDemandComposite(CompositeCountry cc, int year) {
+		if (!ModelConfig.CHANGE_DEMAND_YEAR)
+			year = ModelConfig.BASE_YEAR;
+
+		Map<WoodType, Double> compositeDemandMap = new HashMap<WoodType, Double>();
+		Map<WoodType, Double> singleDemandMap;
+
+		for (SingleCountry c : compositeCountryManager.getAllForCompositeCountry(cc)) {
+			singleDemandMap = getWoodDemand(c, year);
+			for (WoodType w : WoodType.values()) {
+				double totalDemand = (compositeDemandMap.containsKey(w)) ? compositeDemandMap.get(w) : 0.0;
+				double demand = singleDemandMap.get(w);
+				totalDemand += demand;
+				compositeDemandMap.put(w, totalDemand);				
+			}
+		}
+		return compositeDemandMap;
+	};
 
 }
diff --git a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java
index 56da459d243b29d03404694f62d347a9ad460a08..e5bd457850e6771c123551adc4c4160f7f946b85 100644
--- a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java
+++ b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java
@@ -10,6 +10,7 @@ import ac.ed.lurg.country.CompositeCountryManager;
 import ac.ed.lurg.country.CountryManager;
 import ac.ed.lurg.country.SingleCountry;
 import ac.ed.lurg.types.CommodityType;
+import ac.ed.lurg.types.WoodType;
 import ac.ed.lurg.utils.LogWriter;
 
 public abstract class AbstractSSPDemandManager extends AbstractDemandManager {
@@ -86,4 +87,26 @@ 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);
+
+		Map<WoodType, Double> woodDemandMap = new HashMap<WoodType, Double>();
+		
+		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));
+			return woodDemandMap;
+		}
+		
+		double baseGdpPc = baseSd.getGdpPc() * ModelConfig.SSP_GDP_PC_FACTOR;
+		double gdpPc = sd.getGdpPc() * ModelConfig.SSP_GDP_PC_FACTOR;
+		double basePopulation = baseSd.getPopulation() * ModelConfig.SSP_POPULATION_FACTOR;
+		double population = sd.getPopulation() * ModelConfig.SSP_POPULATION_FACTOR;
+		
+		woodDemandMap = getWoodDemandMap(country, gdpPc, baseGdpPc, population, basePopulation);
+		return woodDemandMap;
+	}
+	
+	abstract Map<WoodType, Double> getWoodDemandMap(SingleCountry country, double gdpPc, double baseGdpPc, double population, double basePopulation);
 }
diff --git a/src/ac/ed/lurg/demand/DemandManagerFromFile.java b/src/ac/ed/lurg/demand/DemandManagerFromFile.java
index 7d084ec548586cb3b3587c43cb54dcdbeaf7c286..3fdad7ae9bce7447249d62b1d25cec50f00d3efc 100644
--- a/src/ac/ed/lurg/demand/DemandManagerFromFile.java
+++ b/src/ac/ed/lurg/demand/DemandManagerFromFile.java
@@ -7,6 +7,7 @@ import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.country.CompositeCountryManager;
 import ac.ed.lurg.country.SingleCountry;
 import ac.ed.lurg.types.CommodityType;
+import ac.ed.lurg.types.WoodType;
 import ac.ed.lurg.utils.LogWriter;
 import ac.ed.lurg.utils.StringTabularReader;
 
@@ -47,5 +48,11 @@ public class DemandManagerFromFile extends AbstractDemandManager {
 			return foodDemandMap;
 		}
 		return dietaryAdjustment(foodDemandMap, year, calorieManager.get(c));
+	}
+
+	@Override
+	protected Map<WoodType, Double> getWoodDemand(SingleCountry country, int year) {
+		// TODO Auto-generated method stub
+		return null;
 	}	
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/demand/DemandManagerSSP.java b/src/ac/ed/lurg/demand/DemandManagerSSP.java
index c139c239dc86ef66afd46b17c8e0daf571155f75..833695c185481f0cedbb99678bb2665be44507c0 100644
--- a/src/ac/ed/lurg/demand/DemandManagerSSP.java
+++ b/src/ac/ed/lurg/demand/DemandManagerSSP.java
@@ -8,6 +8,7 @@ import ac.ed.lurg.country.CompositeCountryManager;
 import ac.ed.lurg.country.SingleCountry;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.ModelFitType;
+import ac.ed.lurg.types.WoodType;
 
 public class DemandManagerSSP extends AbstractSSPDemandManager {
 
@@ -37,5 +38,12 @@ public class DemandManagerSSP extends AbstractSSPDemandManager {
 		}
 		
 		return aFoodDemandMap;
+	}
+
+	@Override
+	Map<WoodType, Double> getWoodDemandMap(SingleCountry country, double gdpPc, double baseGdpPc, double population,
+			double basePopulation) {
+		// TODO Auto-generated method stub
+		return null;
 	}	
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/demand/ElasticDemandManager.java b/src/ac/ed/lurg/demand/ElasticDemandManager.java
index eca6b8d87f9eebe4719c4bab89d12e2ccff64cba..e182bc6c7d804385dd5f92464653f502c28ca86f 100755
--- a/src/ac/ed/lurg/demand/ElasticDemandManager.java
+++ b/src/ac/ed/lurg/demand/ElasticDemandManager.java
@@ -13,6 +13,7 @@ import ac.ed.lurg.country.gams.GamsDemandInput;
 import ac.ed.lurg.country.gams.GamsDemandOptimiser;
 import ac.ed.lurg.country.gams.GamsDemandOutput;
 import ac.ed.lurg.types.CommodityType;
+import ac.ed.lurg.types.WoodType;
 import ac.ed.lurg.utils.FileWriterHelper;
 import ac.ed.lurg.utils.LogWriter;
 
@@ -128,4 +129,15 @@ public class ElasticDemandManager extends AbstractSSPDemandManager {
 			LogWriter.print(e);
 		}
 	}
+	
+	@Override
+	public Map<WoodType, Double> getWoodDemandMap(SingleCountry country, double gdpPc, double baseGdpPc, double population, double basePopulation) {
+		Map<WoodType, Double> demandMap = new HashMap<WoodType, Double>();
+		for (WoodType woodType : WoodType.values()) {
+			double baseDemand = woodDemandManager.get(country, woodType);
+			double newDemand = baseDemand * (population / basePopulation) * Math.pow(gdpPc / baseGdpPc, woodType.getIncomeDemandElasticity());
+			demandMap.put(woodType, newDemand);
+		}
+		return demandMap;
+	}
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/demand/TimberDemandManager.java b/src/ac/ed/lurg/demand/TimberDemandManager.java
deleted file mode 100644
index 560ea3bc94548a78cf9774c33a67738fc4c363e9..0000000000000000000000000000000000000000
--- a/src/ac/ed/lurg/demand/TimberDemandManager.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package ac.ed.lurg.demand;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import ac.ed.lurg.ModelConfig;
-import ac.ed.lurg.utils.Interpolator;
-import ac.ed.lurg.utils.LogWriter;
-
-public class TimberDemandManager {
-	private Map<Integer, Double> globalTimberDemand; // global demand for timber
-	private static final int YEAR_COL = 0;
-	private static final int DEMAND_COL = 1; 
-	
-	public TimberDemandManager() {
-		readTimberDemandData();
-	}
-	
-	public void readTimberDemandData() {
-		
-		Map<Integer, Double> data = new HashMap<Integer, Double>();
-		
-		String filename = ModelConfig.TIMBER_DEMAND_FILE;
-		try {
-			BufferedReader reader = new BufferedReader(new FileReader(filename)); 
-			String line;
-			Integer year;
-			Double demand;
-			reader.readLine(); // read header
-
-			while ((line=reader.readLine()) != null) {
-				String[] tokens = line.split(",");
-				
-				if (tokens.length < 2)
-					LogWriter.printlnError("Too few columns in " + filename + ", " + line);
-				
-				year = Integer.valueOf(tokens[YEAR_COL]);
-				demand = Double.valueOf(tokens[DEMAND_COL]);
-				
-				data.put(year,  demand);
-			} 
-			reader.close(); 
-		
-		} catch (IOException e) {
-			LogWriter.printlnError("Failed in reading timber demand data");
-			LogWriter.print(e);
-		}
-		globalTimberDemand = data;
-		LogWriter.println("Processed " + filename);
-	}
-	
-	public double getGlobalTimberDemand(int year) {
-		int downYear = (year/5) * 5;
-		int upYear = downYear + 5;
-		Double lowerD = globalTimberDemand.get(downYear);
-		Double upperD = globalTimberDemand.get(upYear);
-		double factor = ((double)(year - downYear)) / (upYear - downYear);
-		Double d = Interpolator.interpolate(lowerD, upperD, factor);
-		return d;
-	}
-}
diff --git a/src/ac/ed/lurg/demand/WoodDemandManager.java b/src/ac/ed/lurg/demand/WoodDemandManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..b95093a3a91b6191920da7fb47e5203067732031
--- /dev/null
+++ b/src/ac/ed/lurg/demand/WoodDemandManager.java
@@ -0,0 +1,81 @@
+package ac.ed.lurg.demand;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.country.CountryManager;
+import ac.ed.lurg.country.SingleCountry;
+import ac.ed.lurg.types.WoodType;
+import ac.ed.lurg.utils.LogWriter;
+
+public class WoodDemandManager {
+	private static final int COUNTY_COL = 0;
+	private static final int WOOD_TYPE_COL = 1; 
+	private static final int DEMAND_COL = 2; 
+	private static final double WOOD_BIOMASS_CONVERSION_FACTOR = 0.2688; // m3 to tC-eq p.16 [https://doi.org/10.5194/gmd-13-5425-2020]
+	
+	private Map<SingleCountry, Map<WoodType, Double>> baseWoodDemand = new HashMap<SingleCountry, Map<WoodType, Double>>();
+	
+	public WoodDemandManager() {
+		readWoodDemandData();
+	}
+	
+	public void readWoodDemandData() {
+		
+		String filename = ModelConfig.TIMBER_DEMAND_FILE;
+		try {
+			BufferedReader reader = new BufferedReader(new FileReader(filename)); 
+			String line, countryName, woodTypeName;
+			Double demand;
+			reader.readLine(); // read header
+
+			while ((line=reader.readLine()) != null) {
+				String[] tokens = line.split(",");
+				
+				if (tokens.length < 3)
+					LogWriter.printlnError("Too few columns in " + filename + ", " + line);
+				
+				countryName = tokens[COUNTY_COL];
+				woodTypeName = tokens[WOOD_TYPE_COL];
+				demand = Double.valueOf(tokens[DEMAND_COL]);
+				
+				SingleCountry country = CountryManager.getForName(countryName);
+				if (country == null)
+					LogWriter.printlnError("Null country for" + countryName);
+				else {
+					WoodType woodType = WoodType.getForName(woodTypeName);
+					
+					Map<WoodType, Double> woodTypeMap = baseWoodDemand.get(country);
+					if (woodTypeMap == null) {
+						woodTypeMap = new HashMap<WoodType, Double>();
+						baseWoodDemand.put(country, woodTypeMap);
+					}
+					
+					double demandBiomassEq = demand / 1e6 * WOOD_BIOMASS_CONVERSION_FACTOR; // converting from m3 to MtC-eq
+					woodTypeMap.put(woodType, demandBiomassEq);		
+				}
+			} 
+			reader.close(); 
+		
+		} catch (IOException e) {
+			LogWriter.printlnError("Failed in reading wood demand data");
+			LogWriter.print(e);
+		}
+		LogWriter.println("Processed " + filename);
+	}
+	
+	public double get(SingleCountry country, WoodType woodType) {
+		Map<WoodType, Double> woodTypeMap = baseWoodDemand.get(country);
+		if (woodTypeMap != null && woodTypeMap.containsKey(woodType)) {
+			return woodTypeMap.get(woodType).doubleValue();
+		} else {
+			LogWriter.printlnError("BaseConsumpManager: can't get value for " + woodType + ", " + country);
+			return 0.0;
+		}
+	}
+	
+}
diff --git a/src/ac/ed/lurg/landuse/WoodUsageData.java b/src/ac/ed/lurg/landuse/WoodUsageData.java
new file mode 100644
index 0000000000000000000000000000000000000000..2232acd97c23aa74aad946248fa87bbff2a48eb3
--- /dev/null
+++ b/src/ac/ed/lurg/landuse/WoodUsageData.java
@@ -0,0 +1,19 @@
+package ac.ed.lurg.landuse;
+
+public class WoodUsageData {
+	private double harvest;
+	private double netImport;
+	
+	public WoodUsageData(double harvest, double netImport) {
+		this.harvest = harvest;
+		this.netImport = netImport;
+	}
+
+	public double getHarvest() {
+		return harvest;
+	}
+
+	public double getNetImport() {
+		return netImport;
+	}	
+}
diff --git a/src/ac/ed/lurg/types/WoodType.java b/src/ac/ed/lurg/types/WoodType.java
new file mode 100644
index 0000000000000000000000000000000000000000..de4af65fa1e2b7e927146b6f03b68dbc4527a111
--- /dev/null
+++ b/src/ac/ed/lurg/types/WoodType.java
@@ -0,0 +1,43 @@
+package ac.ed.lurg.types;
+
+import java.util.HashMap;
+import java.util.Map;
+import ac.ed.lurg.ModelConfig;
+
+public enum WoodType {
+	ROUNDWOOD("roundwood", ModelConfig.ROUNDWOOD_DEMAND_ELASTICITY),
+	FUELWOOD("fuelwood", ModelConfig.FUELWOOD_DEMAND_ELASTICITY);
+	
+	private String name;
+	private double incomeDemandElasticity;
+	
+	WoodType(String name, double incomeDemandElasticity) {
+		this.name = name;
+		this.incomeDemandElasticity = incomeDemandElasticity;
+	}
+
+	private static final Map<String, WoodType> nameCache = new HashMap<String, WoodType>();
+
+	static {
+		for (WoodType w : values()) {
+			nameCache.put(w.getName(), w);
+		}
+	}
+
+	public static WoodType getForName(String name) {
+		WoodType woodType = nameCache.get(name);
+
+		if (woodType == null) 
+			throw new RuntimeException("Can't find woodType for name: " + name);
+		
+		return woodType;
+	}
+	
+	public String getName() {
+		return name;
+	}
+	
+	public double getIncomeDemandElasticity() {
+		return incomeDemandElasticity;
+	}
+}