diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms
index e1a9e64eefb22bdc99d21189a9b29bfe862495d0..e1e15a44845005ca62ce17ee88b12868a3b16021 100644
--- a/GAMS/IntExtOpt.gms
+++ b/GAMS/IntExtOpt.gms
@@ -3,7 +3,6 @@
  SET crop_less_pasture(crop) arable crops types includes treenuts but not pasture / cereals, fruits, oilcrops, starchyRoots, treenuts, vegetables /;
  SET feed_crop(crop)  / cereals, oilcrops /;
  SET not_feed_crops(crop)  / fruits, starchyRoots, treenuts, vegetables, pasture_or_meat /;
- 
  SET landuse land use type / cropland, pasture /;
      
  PARAMETER previous_area(landuse)    areas for previous timestep in ha;
@@ -12,36 +11,39 @@
  PARAMETER world_input_energy(crop)  average input energy from world exports used to determine if we should import or export;
  PARAMETER maxExport(crop)           max export for each crop based on world market;
  
- SCALAR meatEfficency;
- SCALAR maxLandUseChange;
- SCALAR maxIntensity;
- SCALAR tradeBarrier / 1 / ;
+ SCALAR meatEfficency                efficency of converting feed and pasture into animal products;
+ SCALAR maxLandUseChange             max rate of land use change;
+ SCALAR maxIntensity                 max intensity which will rise over time;
+ SCALAR tradeBarrier                 trade barrier which adjust energy cost of imports;
+ SCALAR landChangeEnergy             energy required to add ha of agricultural land;
  
 $gdxin %gdxincname%
-$load previous_area, demand, yield_ref, world_input_energy, maxExport, meatEfficency, maxLandUseChange, maxIntensity
+$load previous_area, demand, yield_ref, world_input_energy, maxExport, meatEfficency, maxLandUseChange, maxIntensity, tradeBarrier, landChangeEnergy
 $gdxin
-                                  
+ 
+ DISPLAY landChangeEnergy
+ 
  PARAMETER feedEnergy(crop) energy from feed in MJ per t
          /    cereals          1
               oilcrops         1.5
               pasture_or_meat  0.4 / ;
-                            
+ 
  VARIABLES
        area(crop)          total area for each crop - ha
-       feedArea(crop)      area used for feed for each crop - ha
+       feedAmount(crop)    amount of feed for each crop - t
        intensity(crop)     intensity for each crop - unitless
        yield(crop)         yield per area for each crop - t per ha
        unit_energy(crop)   energy per area for each crop - energy
        exportAmount(crop)  export of crop - t
+       importAmount(crop)  export of crop - t
        tradeBarrierFactor(crop)
        energy              total input energy - energy;
-       
- POSITIVE VARIABLE area, intensity, feedArea;
-  
+ 
+ POSITIVE VARIABLE area, intensity, feedAmount, exportAmount, importAmount;
+ 
  EQUATIONS
        UNIT_ENERGY_EQ(crop)                       energy per area
        YIELD_EQ(crop)                             yield given chosen intensity
-       TRADE_BARRIER_FACTOR_EQ
        CROP_DEMAND_CONSTRAINT(crop_less_pasture)  satisfy demand for crop 
        MEAT_DEMAND_CONSTRAINT                     satisfy demand for crop 
        MIN_INTENSITY_CONSTRAINT(crop)             intensity constraint 
@@ -52,34 +54,35 @@ $gdxin
        NON_FEED_CROP_CONSTRAINT(not_feed_crops)   constraint to set non feed crop feed usage to zero
        EXPORT_LEVEL_CONSTRAINT(crop)              constraint on max exports
        ENERGY_EQ                                  total energy objective function;
-       
+ 
  UNIT_ENERGY_EQ(crop) .. unit_energy(crop) =E= intensity(crop) + intensity(crop) / 2 * intensity(crop) / 8;
-  
+ 
  YIELD_EQ(crop) .. yield(crop) =E= yield_ref(crop) * intensity(crop);
  
- TRADE_BARRIER_FACTOR_EQ(crop) .. tradeBarrierFactor(crop) =E= 1;
-  
- CROP_DEMAND_CONSTRAINT(crop_less_pasture) .. (area(crop_less_pasture) - feedArea(crop_less_pasture)) * yield(crop_less_pasture) =G= demand(crop_less_pasture) + exportAmount(crop_less_pasture);
-  
- MEAT_DEMAND_CONSTRAINT .. meatEfficency*sum(feed_crop, feedEnergy(feed_crop) * feedArea(feed_crop) * yield(feed_crop)) + 
-  			feedEnergy('pasture_or_meat') * area('pasture_or_meat') * yield('pasture_or_meat') =G= demand('pasture_or_meat') + exportAmount('pasture_or_meat');
-  
+ CROP_DEMAND_CONSTRAINT(crop_less_pasture) .. area(crop_less_pasture) * yield(crop_less_pasture) - feedAmount(crop_less_pasture) =G= 
+            demand(crop_less_pasture) + exportAmount(crop_less_pasture) - importAmount(crop_less_pasture);
+ 
+ MEAT_DEMAND_CONSTRAINT .. meatEfficency*sum(feed_crop, feedEnergy(feed_crop) * feedAmount(feed_crop)) + feedEnergy('pasture_or_meat') * area('pasture_or_meat') * yield('pasture_or_meat') =G= 
+            demand('pasture_or_meat') + exportAmount('pasture_or_meat') - importAmount('pasture_or_meat');
+ 
  MIN_INTENSITY_CONSTRAINT(crop) .. intensity(crop) =G= 1;
-  
+ 
  MAX_INTENSITY_CONSTRAINT(crop) .. intensity(crop) =L= maxIntensity;
-  
+ 
  CROPLAND_CHANGE_CONSTRAINT .. abs(sum(crop_less_pasture, area(crop_less_pasture))/previous_area('cropland') - 1) =L= maxLandUseChange;
-  
+ 
  PASTURE_CHANGE_CONSTRAINT ..  abs(area('pasture_or_meat')/previous_area('pasture') - 1) =L= maxLandUseChange;
-  
+ 
  AGRI_LAND_CHANGE_CONSTRAINT .. abs(sum(crop, area(crop)) / sum(landuse, previous_area(landuse)) - 1) =L= maxLandUseChange;
  
- NON_FEED_CROP_CONSTRAINT(not_feed_crops) .. feedArea(not_feed_crops) =E= 0;
+ NON_FEED_CROP_CONSTRAINT(not_feed_crops) .. feedAmount(not_feed_crops) =E= 0;
  
  EXPORT_LEVEL_CONSTRAINT(crop) .. exportAmount(crop) =L= maxExport(crop);
-  
- ENERGY_EQ .. energy =E= SUM(crop, area(crop)*unit_energy(crop)) -  sum(crop, exportAmount(crop) * world_input_energy(crop) * tradeBarrierFactor(crop));
-  
+ 
+ ENERGY_EQ .. energy =E= SUM(crop, area(crop)*unit_energy(crop))
+            + (sum(crop, area(crop)) - sum(landuse, previous_area(landuse))) * landChangeEnergy
+            - sum(crop, (exportAmount(crop) - importAmount(crop) * tradeBarrier) * world_input_energy(crop));
+ 
  MODEL LAND_USE /ALL/ ;
  SOLVE LAND_USE USING DNLP MINIMIZING energy;
  
diff --git a/src/ac/ed/lurg/GamsTest.java b/src/ac/ed/lurg/GamsTest.java
index 9eca86acb1308373ffbf3ccef927f8b05e567f2f..ae8a36e9df5366e650c26b252ff1e1fc241ce32e 100644
--- a/src/ac/ed/lurg/GamsTest.java
+++ b/src/ac/ed/lurg/GamsTest.java
@@ -2,6 +2,8 @@ package ac.ed.lurg;
 
 import java.io.File;
 
+import ac.ed.lurg.utils.LogWriter;
+
 import com.gams.api.GAMSDatabase;
 import com.gams.api.GAMSGlobals;
 import com.gams.api.GAMSJob;
@@ -14,7 +16,7 @@ import com.gams.api.GAMSWorkspaceInfo;
 
 public class GamsTest {
 
-	private final static String WORKING_DIR = "/Users/peteralexander/Documents/R_Workspace/tempDir";
+	private final static String WORKING_DIR = "/Users/peteralexander/Documents/R_Workspace/temp";
 
 	public static void main(String[] args)  {
 		GamsTest aGamsTest = new GamsTest();
@@ -33,8 +35,8 @@ public class GamsTest {
 		GAMSDatabase db = ws.addDatabase();
 
 		GAMSParameter parm = db.addParameter("previous_area", 1, "the previous area for each land use");
-		parm.addRecord("cropland").setValue(200.0);
-		parm.addRecord("pasture").setValue(600.0);
+		parm.addRecord("cropland").setValue(300.0);
+		parm.addRecord("pasture").setValue(400.0);
 		
 		parm = db.addParameter("demand", 1, "demand for crop");
 		parm.addRecord("cereals").setValue(200.0);
@@ -43,7 +45,7 @@ public class GamsTest {
 		parm.addRecord("starchyRoots").setValue(100.0);
 		parm.addRecord("treenuts").setValue(20.0);
 		parm.addRecord("vegetables").setValue(50.0);
-		parm.addRecord("pasture_or_meat").setValue(500);  // meat demand in t
+		parm.addRecord("pasture_or_meat").setValue(200);  // meat demand in t
 
 		parm = db.addParameter("yield_ref", 1, "ref yield t per ha");
 		parm.addRecord("cereals").setValue(3.0);
@@ -55,16 +57,16 @@ public class GamsTest {
 		parm.addRecord("pasture_or_meat").setValue(0.7);  // grass yield 
 		
 		parm = db.addParameter("world_input_energy", 1, "average input energy from world exports used to determine if we should import or export energy per t");
-		parm.addRecord("cereals").setValue(0.45);
+		parm.addRecord("cereals").setValue(4);
 		parm.addRecord("fruits").setValue(10.0);
-		parm.addRecord("oilcrops").setValue(2.0);
-		parm.addRecord("starchyRoots").setValue(0.3);
-		parm.addRecord("treenuts").setValue(2.0);
-		parm.addRecord("vegetables").setValue(2);
-		parm.addRecord("pasture_or_meat").setValue(5); // meat input energy
+		parm.addRecord("oilcrops").setValue(20.0);
+		parm.addRecord("starchyRoots").setValue(10.1265);
+		parm.addRecord("treenuts").setValue(20.0);
+		parm.addRecord("vegetables").setValue(20);
+		parm.addRecord("pasture_or_meat").setValue(50); // meat input energy
 
 		parm = db.addParameter("maxExport", 1, "max export for each crop based on world market");
-		parm.addRecord("cereals").setValue(100);
+		parm.addRecord("cereals").setValue(0);
 		parm.addRecord("fruits").setValue(0);
 		parm.addRecord("oilcrops").setValue(0);
 		parm.addRecord("starchyRoots").setValue(0);
@@ -75,6 +77,8 @@ public class GamsTest {
 		db.addParameter("meatEfficency", 0, "efficency of converting feed and pasture into animal products").addRecord().setValue(0.1);
 		db.addParameter("maxLandUseChange", 0, "max rate of land use change").addRecord().setValue(0.1);
 		db.addParameter("maxIntensity", 0, "max intensity which will rise over time").addRecord().setValue(3);
+		db.addParameter("tradeBarrier", 0, "trade barrier which adjust energy cost of imports").addRecord().setValue(3);
+		db.addParameter("landChangeEnergy", 0, "energy required to add ha of agricultural land").addRecord().setValue(0.08);
 
 		
 		// create GAMSJob "t2" from the "model" string variable
@@ -89,22 +93,28 @@ public class GamsTest {
 		
 		GAMSVariable varAreas = gamsJob.OutDB().getVariable("area");
 		GAMSVariable varIntensities = gamsJob.OutDB().getVariable("intensity");
-		GAMSVariable varFeedArea = gamsJob.OutDB().getVariable("feedArea");
+		GAMSVariable varFeedAmount = gamsJob.OutDB().getVariable("feedAmount");
 		GAMSVariable varExports = gamsJob.OutDB().getVariable("exportAmount");
+		GAMSVariable varImports = gamsJob.OutDB().getVariable("importAmount");
 
+		double totalArea = 0;
+		
 		for (GAMSVariableRecord rec : varAreas) {
-			System.out.println(String.format("%s: \t\tarea= %.0f, \tintensity= %.3f, \tfeed_area= %.0f, \texports= %.0f", 
+			LogWriter.println(String.format("%15s:\tarea= %.0f,\tintensity= %.3f,\tfeedAmount= %.0f,\texports= %.0f,\timports= %.0f", 
 					rec.getKeys()[0], rec.getLevel(), 
 					varIntensities.findRecord(rec.getKeys()[0]).getLevel(), 
-					varFeedArea.findRecord(rec.getKeys()[0]).getLevel(),
-					varExports.findRecord(rec.getKeys()[0]).getLevel()
+					varFeedAmount.findRecord(rec.getKeys()[0]).getLevel(),
+					varExports.findRecord(rec.getKeys()[0]).getLevel(),
+					varImports.findRecord(rec.getKeys()[0]).getLevel()
 					)); 
+			totalArea += rec.getLevel();
 			// + " marginal=" + rec.getMarginal());
 		}
-
+		LogWriter.println("Total Area: " + totalArea);
 		//cleanup(ws.workingDirectory());
 	}
 	
+	@SuppressWarnings("unused")
 	private void cleanup(String directory)  {
 		File directoryToDelete = new File(directory);
 		String files[] = directoryToDelete.list();
diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd131e81fc4111672224cb6dde34191f17dc64fe
--- /dev/null
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -0,0 +1,98 @@
+package ac.ed.lurg;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class ModelConfig {
+	
+	private Properties configFile;
+	private static ModelConfig modelConfig;
+	public static final String CONFIG_FILE = System.getProperty("CONFIG_FILE");
+	
+	private ModelConfig() {
+		configFile = new Properties();
+		try {
+			System.out.println("Config. file is " + CONFIG_FILE);
+			if (CONFIG_FILE != null)
+				configFile.load(new FileInputStream(CONFIG_FILE));
+		}
+		catch (IOException e) {
+			System.err.println("Problems reading config file");
+			System.err.println(e.getMessage());
+		}
+	}
+	
+	private static ModelConfig getModelConfig() {
+		if (modelConfig == null)
+			modelConfig = new ModelConfig();
+
+		return modelConfig;
+	}
+	
+	private static String getProperty(String prop) {
+		return getModelConfig().getProp(prop);
+	}
+	private static String getProperty(String prop, String defaultString) {
+		String propValue = getProperty(prop);
+		return propValue == null ? defaultString : propValue;
+	}
+	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;
+	}
+	private Integer getIntProp(String prop) {
+		String v = configFile.getProperty(prop);
+		return v==null ? null : Integer.valueOf(v);
+	}
+	
+	private static Long getLongProperty(String prop, Long defaultLong) {
+		Long propValue = getModelConfig().getLongProp(prop);
+		return propValue == null ? defaultLong : propValue;
+	}
+	private Long getLongProp(String prop) {
+		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;
+	}
+	private Double getDoubleProp(String prop) {
+		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 final boolean SUPPRESS_STD_OUTPUT = getBooleanProperty("SUPPRESS_STD_OUTPUT", Boolean.FALSE);
+
+	public static final String BASE_DIR = getProperty("OUTPUT_DIR", "/Users/peteralexander/Documents/R_Workspace");
+	public static final String TEMP_DIR = BASE_DIR + File.separator + "temp";
+	public static final String OUTPUT_DIR = BASE_DIR + File.separator + "output";
+	public static final String DATA_DIR = BASE_DIR + File.separator + "data";
+	
+	public static final String DEMAND_CURVES_FILE = DATA_DIR + File.separator + "com_curves.csv";
+	public static final String SSP_FILE = DATA_DIR + File.separator + "ssp.csv";
+	public static final String COUNTRY_DATA_FILE = DATA_DIR + File.separator + "country_data.csv";
+
+	
+
+			
+	public static final int START_TIMESTEP = getIntProperty("START_TIMESTEP", 0);
+	public static final int END_TIMESTEP = getIntProperty("END_TIMESTEP", 36);
+	public static final int BASE_YEAR = getIntProperty("BASE_YEAR", 2010);
+
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/ModelContext.java b/src/ac/ed/lurg/ModelContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..703a40302e2423a3daab0529a8119e6e913a990e
--- /dev/null
+++ b/src/ac/ed/lurg/ModelContext.java
@@ -0,0 +1,23 @@
+package ac.ed.lurg;
+
+import ac.ed.lurg.demand.DemandManager;
+import ac.ed.lurg.yield.YieldManager;
+
+public class ModelContext {
+	private DemandManager demandManager;
+	private YieldManager yieldManager;
+	
+	public ModelContext(DemandManager demandManager, YieldManager yieldManager) {
+		super();
+		this.demandManager = demandManager;
+		this.yieldManager = yieldManager;
+	}
+
+	public DemandManager getDemandManager() {
+		return demandManager;
+	}
+
+	public YieldManager getYieldManager() {
+		return yieldManager;
+	}
+}
diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4ef407565e3f459fb33040c7842feeff1397d53
--- /dev/null
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -0,0 +1,48 @@
+package ac.ed.lurg;
+
+import java.util.Collection;
+
+import ac.ed.lurg.country.CountryAgent;
+import ac.ed.lurg.country.CountryAgentCreator;
+import ac.ed.lurg.demand.DemandManager;
+import ac.ed.lurg.types.ModelFitType;
+import ac.ed.lurg.yield.DummyYieldManager;
+import ac.ed.lurg.yield.YieldManager;
+
+public class ModelMain {
+
+	private Collection<CountryAgent> countryAgents;
+	ModelContext modelContext;
+	
+	public static void main(String[] args)  {
+		ModelMain theModel = new ModelMain();
+		theModel.setup();
+		theModel.run();
+	}
+		
+	/* setup models, reading inputs, etc. */
+	private void setup() {
+		DemandManager demandManager = new DemandManager(ModelFitType.get("logistic"), "SSP2_v9_130325");
+		
+		YieldManager yieldManager = new DummyYieldManager();  // would actually create something for a desired scenario
+		
+		modelContext = new ModelContext(demandManager, yieldManager);
+		
+		// read initial country data (country, areas)
+		countryAgents = new CountryAgentCreator().getAgents(modelContext);
+	}
+	
+	/* run the model */
+	private void run() {
+		for (int i = ModelConfig.START_TIMESTEP; i <= ModelConfig.END_TIMESTEP; i++)
+			doTimestep(i);
+	}
+
+	private void doTimestep(int timestep) {
+		for (CountryAgent ca : countryAgents) {
+			ca.determineProduction(timestep);
+		}
+		
+		// examine global trade balance
+	}	
+}
diff --git a/src/ac/ed/lurg/country/Country.java b/src/ac/ed/lurg/country/Country.java
new file mode 100644
index 0000000000000000000000000000000000000000..0337033e7175b761b6a95710bd341137d171a9e6
--- /dev/null
+++ b/src/ac/ed/lurg/country/Country.java
@@ -0,0 +1,57 @@
+package ac.ed.lurg.country;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Country {
+
+	private String countryName;
+	private static final Map<String, Country> cache = new HashMap<String, Country>();
+
+	private Country(String countryName) {
+		super();
+		this.countryName = countryName;
+	}
+	
+	
+	public static Country get(String name) {
+		Country type = cache.get(name);
+		
+		if (type == null) {
+			type = new Country(name);
+			cache.put(name, type);
+		}
+		
+		return type;
+	}
+
+	public String getCountryName() {
+		return countryName;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((countryName == null) ? 0 : countryName.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Country other = (Country) obj;
+		if (countryName == null) {
+			if (other.countryName != null)
+				return false;
+		} else if (!countryName.equals(other.countryName))
+			return false;
+		return true;
+	}
+}
diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
new file mode 100644
index 0000000000000000000000000000000000000000..405b0fd1780c87c7bf108fa9df01babd953f054f
--- /dev/null
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -0,0 +1,50 @@
+package ac.ed.lurg.country;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.ModelContext;
+import ac.ed.lurg.types.Item;
+import ac.ed.lurg.utils.LogWriter;
+
+/*
+ * Contains data for a country (perhaps a entire timeseries?!)
+ */
+public class CountryAgent {
+	
+	private ModelContext modelContext;
+	private Country country;
+	private Map<Integer, CountryData> timeseries = new HashMap<Integer, CountryData>();
+
+	public CountryAgent(ModelContext modelContext, Country country, CountryData initialCountryData) {
+		this.modelContext = modelContext;
+		this.country = country;
+		timeseries.put(0, initialCountryData);
+	}
+	
+	public Country getCountry() {
+		return country;
+	}
+
+	public double getCropArea(int timestep, Item item) {
+		CountryData cd = timeseries.get(timestep);
+		if (cd == null) {
+			LogWriter.printlnError("Asked for timestep that we don't have " + timestep);
+			return Double.NaN;
+		}
+		
+		return cd.getCropArea(item);
+	}
+
+	public void determineProduction(int timestep) {
+		// get projected demand
+		Map<Item, Double> projectedDemand = modelContext.getDemandManager().getDemand(country, ModelConfig.BASE_YEAR + timestep);
+		
+		// get projected ref yields
+	
+		// optimise areas and intensity 
+		
+		
+	}
+}
diff --git a/src/ac/ed/lurg/country/CountryAgentCreator.java b/src/ac/ed/lurg/country/CountryAgentCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..52da9d7ac5a657dea6b539a00cdaa94e8169d8f1
--- /dev/null
+++ b/src/ac/ed/lurg/country/CountryAgentCreator.java
@@ -0,0 +1,69 @@
+package ac.ed.lurg.country;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.Collection;
+import java.util.HashSet;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.ModelContext;
+import ac.ed.lurg.types.LandCoverType;
+import ac.ed.lurg.utils.LogWriter;
+
+public class CountryAgentCreator {
+	
+	private static final int COUNTRY_COL = 0; 
+	private static final int YEAR_COL = 1; 
+//  private static final int REGION_COL = 2; 
+//	private static final int INCOME_GROUP_COL = 3; 
+	private static final int LAND_COL = 4; 
+	private static final int ARABLE_COL = 5; 
+	private static final int FOREST_COL = 7; 
+	private static final int PERM_CROP_COL = 8; 
+	private static final int PASTURE_COL = 9; 
+
+	public Collection<CountryAgent> getAgents(ModelContext modelContext) {
+		Collection<CountryAgent> countryAgents = new HashSet<CountryAgent>();
+		String filename = ModelConfig.COUNTRY_DATA_FILE;
+		try {
+			BufferedReader fitReader = new BufferedReader(new FileReader(filename)); 
+			String line, countryName;
+			int year;
+			
+			fitReader.readLine(); // read header
+
+			while ((line=fitReader.readLine()) != null) {
+				String[] tokens = line.split(",");
+				
+				if (tokens.length < 10)
+					LogWriter.printlnError("Too few columns in " + filename + ", " + line);
+				
+				countryName = tokens[COUNTRY_COL];
+				year = Integer.parseInt(tokens[YEAR_COL]);
+				
+				if (year != ModelConfig.BASE_YEAR) {
+					LogWriter.printlnError("Baseline country data year (" + year + ") does not match configured year");
+				}
+				
+				CountryData initialCountryData = new CountryData();
+				initialCountryData.setLandCoverArea(LandCoverType.LAND, Double.parseDouble(tokens[LAND_COL]));
+				initialCountryData.setLandCoverArea(LandCoverType.ARABLE, Double.parseDouble(tokens[ARABLE_COL]));
+				initialCountryData.setLandCoverArea(LandCoverType.FOREST, Double.parseDouble(tokens[FOREST_COL]));
+				initialCountryData.setLandCoverArea(LandCoverType.PERM_CROP, Double.parseDouble(tokens[PERM_CROP_COL]));
+				initialCountryData.setLandCoverArea(LandCoverType.PASTURE, Double.parseDouble(tokens[PASTURE_COL]));
+
+				CountryAgent ca = new CountryAgent(modelContext, Country.get(countryName), initialCountryData);
+						
+				countryAgents.add(ca);
+			} 
+			fitReader.close(); 
+			
+		} catch (Exception e) {
+			LogWriter.printlnError("Failed in reading commodity demand fits");
+			e.printStackTrace();
+		}
+		
+		LogWriter.println("Processed " + filename + ", create " + countryAgents.size() + " country agents");
+		return countryAgents;
+	}
+}
diff --git a/src/ac/ed/lurg/country/CountryData.java b/src/ac/ed/lurg/country/CountryData.java
new file mode 100644
index 0000000000000000000000000000000000000000..845f59136b66b781c59abc58e4fa96d2ac78087d
--- /dev/null
+++ b/src/ac/ed/lurg/country/CountryData.java
@@ -0,0 +1,34 @@
+package ac.ed.lurg.country;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ac.ed.lurg.types.Item;
+import ac.ed.lurg.types.LandCoverType;
+
+/*
+ * Data for one year and country, but it does not know which one
+ */
+public class CountryData {
+
+	private Map<Item, Double> cropAreas = new HashMap<Item, Double>();
+	private Map<LandCoverType, Double> landCoverAreas = new HashMap<LandCoverType, Double>();
+
+	public double getLandCoverArea(LandCoverType c) {
+		Double d = landCoverAreas.get(c);
+		return d == null ? Double.NaN : d;
+	}
+	
+	public void setLandCoverArea(LandCoverType c, double d) {
+		landCoverAreas.put(c, d);
+	}
+
+	public double getCropArea(Item c) {
+		Double d = cropAreas.get(c);
+		return d == null ? Double.NaN : d;
+	}
+	
+	public void setCropArea(Item c, double d) {
+		cropAreas.put(c, d);
+	}
+}
diff --git a/src/ac/ed/lurg/demand/DemandCurve.java b/src/ac/ed/lurg/demand/DemandCurve.java
new file mode 100644
index 0000000000000000000000000000000000000000..c704f8d8526f76897c25896ae380c9ba1c5e06af
--- /dev/null
+++ b/src/ac/ed/lurg/demand/DemandCurve.java
@@ -0,0 +1,78 @@
+package ac.ed.lurg.demand;
+
+import ac.ed.lurg.types.Item;
+import ac.ed.lurg.types.ModelFitType;
+
+
+public class DemandCurve {
+
+	private double a;
+	private double b;
+	private double c;
+	private ModelFitType fitType;
+	private Item item;
+
+	DemandCurve(Item item, double a, double b, double c, ModelFitType fitType) {
+		super();
+		this.item = item;
+		this.a = a;
+		this.b = b;
+		this.c = c;
+		this.fitType = fitType;
+	}
+	
+	public double getA() {
+		return a;
+	}
+
+	public double getB() {
+		return b;
+	}
+
+	public double getC() {
+		return c;
+	}
+
+	public ModelFitType getFitType() {
+		return fitType;
+	}
+
+	public Item getItem() {
+		return item;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((fitType == null) ? 0 : fitType.hashCode());
+		result = prime * result + ((item == null) ? 0 : item.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		DemandCurve other = (DemandCurve) obj;
+		if (fitType == null) {
+			if (other.fitType != null)
+				return false;
+		} else if (!fitType.equals(other.fitType))
+			return false;
+		if (item == null) {
+			if (other.item != null)
+				return false;
+		} else if (!item.equals(other.item))
+			return false;
+		return true;
+	}
+
+	public double project(double gdpPc) {
+		return a/(1+Math.exp((b-gdpPc)/c));
+	}
+}
diff --git a/src/ac/ed/lurg/demand/DemandCurveManager.java b/src/ac/ed/lurg/demand/DemandCurveManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..75d3f8a2d2710413e764d7863d5d9b55ae7d9781
--- /dev/null
+++ b/src/ac/ed/lurg/demand/DemandCurveManager.java
@@ -0,0 +1,69 @@
+package ac.ed.lurg.demand;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.Collection;
+import java.util.HashSet;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.types.Item;
+import ac.ed.lurg.types.ModelFitType;
+import ac.ed.lurg.utils.LogWriter;
+
+public class DemandCurveManager {
+	private static final int ITEM_COL = 0; 
+	private static final int A_COL = 1; 
+	private static final int B_COL = 2; 
+	private static final int C_COL = 3; 
+	private static final int FIT_TYPE_COL = 6; 
+	
+	private Collection<DemandCurve> curves = new HashSet<DemandCurve>();
+
+	public DemandCurveManager() {
+		super();
+		read();
+	}
+	
+	public void read() {
+		String filename = ModelConfig.DEMAND_CURVES_FILE;
+		try {
+			BufferedReader fitReader = new BufferedReader(new FileReader(filename)); 
+			String line, item, fitType;
+			double a, b, c;
+			fitReader.readLine(); // read header
+
+			while ((line=fitReader.readLine()) != null) {
+				String[] tokens = line.split(",");
+				
+				if (tokens.length < 7)
+					LogWriter.printlnError("Too few columns in " + filename + ", " + line);
+				
+				item = tokens[ITEM_COL];
+				a = Double.parseDouble(tokens[A_COL]);
+				b = Double.parseDouble(tokens[B_COL]);
+				c = Double.parseDouble(tokens[C_COL]);
+				fitType = tokens[FIT_TYPE_COL];
+
+				DemandCurve dc = new DemandCurve(Item.get(item), a, b, c, ModelFitType.get(fitType));
+				curves.add(dc);
+			} 
+			fitReader.close(); 
+		
+		} catch (Exception e) {
+			LogWriter.printlnError("Failed in reading commodity demand fits");
+			e.printStackTrace();
+		}
+		LogWriter.println("Processed " + filename + ", create " + curves.size() + " demand curves");
+	}
+	
+	public Collection<DemandCurve> get(ModelFitType fitType) {
+		Collection<DemandCurve> fitTypeCurves = new HashSet<DemandCurve>();
+		
+		for (DemandCurve dc : curves) {
+			if (dc.getFitType().equals(fitType)) {
+				fitTypeCurves.add(dc);
+			}
+		}	
+		return fitTypeCurves;
+	}
+}
diff --git a/src/ac/ed/lurg/demand/DemandManager.java b/src/ac/ed/lurg/demand/DemandManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..3654c1bbdbe3a5e90fd1071fec11bc8f4b637efa
--- /dev/null
+++ b/src/ac/ed/lurg/demand/DemandManager.java
@@ -0,0 +1,39 @@
+package ac.ed.lurg.demand;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ac.ed.lurg.country.Country;
+import ac.ed.lurg.types.Item;
+import ac.ed.lurg.types.ModelFitType;
+
+public class DemandManager {
+
+	private DemandCurveManager demandCurveManager;
+	private SspManager sspManager;
+	
+	private ModelFitType fitType;  // these could be mutable
+	private String ssp_scenario;
+
+	public DemandManager (ModelFitType fitType, String ssp_scenario) {
+		this.fitType = fitType;
+		this.ssp_scenario = ssp_scenario;
+		demandCurveManager = new DemandCurveManager();
+		sspManager = new SspManager();
+	}
+	
+	public Map<Item, Double> getDemand(Country country, int year) {
+		Map<Item, Double> demandMap = new HashMap<Item, Double>();
+		SspData sd = sspManager.get(ssp_scenario, year, country);
+
+		for (DemandCurve dc : demandCurveManager.get(fitType)) {
+			double d = dc.project(sd.getGdpPc());
+			demandMap.put(dc.getItem(), d);
+		}
+		return demandMap;
+	}
+
+	public String getModelAndScenarioDescription() {
+		return fitType + " " + ssp_scenario;
+	}
+}
diff --git a/src/ac/ed/lurg/demand/SspData.java b/src/ac/ed/lurg/demand/SspData.java
new file mode 100644
index 0000000000000000000000000000000000000000..5dedb78ba40cc6346812e6af15c4dc6ff6b7934e
--- /dev/null
+++ b/src/ac/ed/lurg/demand/SspData.java
@@ -0,0 +1,25 @@
+package ac.ed.lurg.demand;
+
+
+public class SspData {
+	
+	private double population;
+	private double gdp;
+	
+	public SspData(double population, double gdp) {
+		super();
+		this.population = population;
+		this.gdp = gdp;
+	}
+	
+	public double getPopulation() {
+		return population;
+	}
+	public double getGdp() {
+		return gdp;
+	}
+
+	public double getGdpPc() {
+		return gdp/population;
+	}
+}
diff --git a/src/ac/ed/lurg/demand/SspKey.java b/src/ac/ed/lurg/demand/SspKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..725284bd07cedf2fd023451b625d87eccf6b7062
--- /dev/null
+++ b/src/ac/ed/lurg/demand/SspKey.java
@@ -0,0 +1,63 @@
+package ac.ed.lurg.demand;
+
+import ac.ed.lurg.country.Country;
+
+public class SspKey {
+	private String scenario;
+	private int year;
+	private Country country;
+	
+	public SspKey(String scenario, int year, Country country) {
+		super();
+		this.scenario = scenario;
+		this.year = year;
+		this.country = country;
+	}
+
+	public String getScenario() {
+		return scenario;
+	}
+
+	public int getYear() {
+		return year;
+	}
+
+	public Country getCountry() {
+		return country;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((country == null) ? 0 : country.hashCode());
+		result = prime * result
+				+ ((scenario == null) ? 0 : scenario.hashCode());
+		result = prime * result + year;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		SspKey other = (SspKey) obj;
+		if (country == null) {
+			if (other.country != null)
+				return false;
+		} else if (!country.equals(other.country))
+			return false;
+		if (scenario == null) {
+			if (other.scenario != null)
+				return false;
+		} else if (!scenario.equals(other.scenario))
+			return false;
+		if (year != other.year)
+			return false;
+		return true;
+	}
+}
diff --git a/src/ac/ed/lurg/demand/SspManager.java b/src/ac/ed/lurg/demand/SspManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..0699b074d4e199501903122fb3d17fc88bd423e4
--- /dev/null
+++ b/src/ac/ed/lurg/demand/SspManager.java
@@ -0,0 +1,74 @@
+package ac.ed.lurg.demand;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.country.Country;
+import ac.ed.lurg.utils.LogWriter;
+
+public class SspManager {
+//	private static final int MODEL_COL = 0; 
+	private static final int SCENARIO_COL = 1; 
+	private static final int YEAR_COL = 2; 
+	private static final int POPULATION_COL = 3; 
+	private static final int COUNTRY_COL = 4; 
+	private static final int GDP_COL = 5; 
+
+	private Map<SspKey, SspData> sspMap = new HashMap<SspKey, SspData>();
+
+	public SspManager() {
+		super();
+		read();
+	}
+	
+	public void read() {
+		String filename = ModelConfig.SSP_FILE;
+		try {
+			BufferedReader reader = new BufferedReader(new FileReader(filename)); 
+			String line, scenario, countryName;
+			double population, gdp;
+			int year;
+			reader.readLine(); // read header
+
+			while ((line=reader.readLine()) != null) {
+				String[] tokens = line.split(",");
+				
+				if (tokens.length < 6)
+					LogWriter.printlnError("Too few columns in " + filename + ", " + line);
+				
+				try {
+				//model = tokens[MODEL_COL];
+					scenario = tokens[SCENARIO_COL];
+					year = Integer.parseInt(tokens[YEAR_COL]);
+					population = Double.parseDouble(tokens[POPULATION_COL]);
+					countryName = tokens[COUNTRY_COL];
+					gdp = Double.parseDouble(tokens[GDP_COL]);
+	
+					SspKey sspKey = new SspKey(scenario, year, Country.get(countryName));
+					SspData sspData = new SspData(population, gdp);
+					sspMap.put(sspKey, sspData);
+				} catch (Exception e) {
+					LogWriter.printlnError("Failed in processing ssp line " + line);
+					e.printStackTrace();
+				} 
+			} 
+			reader.close(); 
+		
+		} catch (Exception e) {
+			LogWriter.printlnError("Failed in reading ssp data file");
+			e.printStackTrace();
+		} 
+		LogWriter.println("Processed " + filename + ", create " + sspMap.size() + " SSP entries");
+	}
+	
+	public SspData get(String scenario, int year, Country country) {
+		return get(new SspKey(scenario, year, country));
+	}
+
+	public SspData get(SspKey sspKey) {
+		return sspMap.get(sspKey);
+	}
+}
diff --git a/src/ac/ed/lurg/types/AbstractNameType.java b/src/ac/ed/lurg/types/AbstractNameType.java
new file mode 100644
index 0000000000000000000000000000000000000000..86e3f4f41f3d54b3289215e2d76cc01337dba99f
--- /dev/null
+++ b/src/ac/ed/lurg/types/AbstractNameType.java
@@ -0,0 +1,40 @@
+package ac.ed.lurg.types;
+
+
+public abstract class AbstractNameType {
+	String name;
+	
+	protected AbstractNameType(String name) {
+		this.name = name;
+	}
+	
+	public String toString() {
+		return getClass().getName() + " [" + name + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		AbstractNameType other = (AbstractNameType) obj;
+		if (name == null) {
+			if (other.name != null)
+				return false;
+		} else if (!name.equals(other.name))
+			return false;
+		return true;
+	}
+}
+
diff --git a/src/ac/ed/lurg/types/Item.java b/src/ac/ed/lurg/types/Item.java
new file mode 100644
index 0000000000000000000000000000000000000000..d54fc854b6d42919f2e386d01448b4187c801f0a
--- /dev/null
+++ b/src/ac/ed/lurg/types/Item.java
@@ -0,0 +1,29 @@
+package ac.ed.lurg.types;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Item extends AbstractNameType{
+
+	private static final Map<String, Item> cache = new HashMap<String, Item>();
+	
+	public Item(String name) {
+		super(name);
+	}
+
+	public static Item get(String name) {
+		Item type = cache.get(name);
+		
+		if (type == null) {
+			type = new Item(name);
+			cache.put(name, type);
+		}
+		
+		return type;
+	}
+
+	public static Collection<Item> getAllItems() {
+		return cache.values();
+	}
+}
diff --git a/src/ac/ed/lurg/types/LandCoverType.java b/src/ac/ed/lurg/types/LandCoverType.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2b89867d29c41770bfe50eccd6d75291e7cb800
--- /dev/null
+++ b/src/ac/ed/lurg/types/LandCoverType.java
@@ -0,0 +1,11 @@
+package ac.ed.lurg.types;
+
+
+public enum LandCoverType {
+	LAND,
+	ARABLE,
+	FOREST,
+	PERM_CROP,
+	PASTURE
+}
+
diff --git a/src/ac/ed/lurg/types/ModelFitType.java b/src/ac/ed/lurg/types/ModelFitType.java
new file mode 100644
index 0000000000000000000000000000000000000000..2afd0d8831e8e1d790ee93a77a3ae3c2aa0634a5
--- /dev/null
+++ b/src/ac/ed/lurg/types/ModelFitType.java
@@ -0,0 +1,24 @@
+package ac.ed.lurg.types;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ModelFitType extends AbstractNameType {
+
+	private static final Map<String, ModelFitType> cache = new HashMap<String, ModelFitType>();
+	
+	private ModelFitType(String name) {
+		super(name);
+	}
+	
+	public static ModelFitType get(String name) {
+		ModelFitType type = cache.get(name);
+		
+		if (type == null) {
+			type = new ModelFitType(name);
+			cache.put(name, type);
+		}
+		
+		return type;
+	}
+}
diff --git a/src/ac/ed/lurg/utils/LogWriter.java b/src/ac/ed/lurg/utils/LogWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..9031eb8f87354f5f8ab4df103464b0dbb89bab8b
--- /dev/null
+++ b/src/ac/ed/lurg/utils/LogWriter.java
@@ -0,0 +1,73 @@
+package ac.ed.lurg.utils;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import ac.ed.lurg.ModelConfig;
+
+public class LogWriter {
+
+	private static LogWriter logWriter;
+	private BufferedWriter logFile;
+	private boolean suppressStd;
+
+	public static synchronized void println(String s) {
+		getLogWriter().print(s, System.out, true);
+	}
+
+	public static synchronized void print(String s) {
+		getLogWriter().print(s, System.out, false);
+	}
+	
+	public static synchronized void printlnError(String s) {
+		getLogWriter().print(s, System.err, true);
+	}
+
+	private static LogWriter getLogWriter() {
+		if (logWriter == null)
+			logWriter = new LogWriter(ModelConfig.OUTPUT_DIR + File.separator + "log.txt");
+
+		return logWriter;
+	}
+
+	private LogWriter(String outputFileName) {
+		FileWriter fstream;
+		try {
+			fstream = new FileWriter(outputFileName,false);
+			logFile = new BufferedWriter(fstream);
+			suppressStd = ModelConfig.SUPPRESS_STD_OUTPUT;
+		}
+		catch (IOException e) {
+			e.printStackTrace();
+		}		
+	}
+
+	private void print(String s, PrintStream stream, boolean newLine) {
+		try {
+			logFile.write(s);
+			if (newLine) logFile.newLine();
+			logFile.flush();
+		}
+		catch (IOException e) {
+			e.printStackTrace();
+		}
+		
+		if (!suppressStd) {
+			stream.print(s);
+			if (newLine) stream.println();
+		}
+	}
+
+	// probably not called, but never mind as keep stream flushed
+	protected void finalize() {
+		try {
+			logFile.close();
+		}
+		catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/yield/DummyYieldManager.java b/src/ac/ed/lurg/yield/DummyYieldManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4dbd05bbf408f456b67cd4417f3ff8011af03d2
--- /dev/null
+++ b/src/ac/ed/lurg/yield/DummyYieldManager.java
@@ -0,0 +1,19 @@
+package ac.ed.lurg.yield;
+
+import ac.ed.lurg.country.Country;
+import ac.ed.lurg.types.Item;
+
+public class DummyYieldManager implements YieldManager {
+
+	@Override
+	public double getRefYield(Country country, int year, Item commodity) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public String getModelAndScenarioDescription() {
+		return "Dummy yield model";
+	}
+
+}
diff --git a/src/ac/ed/lurg/yield/YieldManager.java b/src/ac/ed/lurg/yield/YieldManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b7ee8d92e9771408455c1e8a23fc8867d76da7d
--- /dev/null
+++ b/src/ac/ed/lurg/yield/YieldManager.java
@@ -0,0 +1,12 @@
+package ac.ed.lurg.yield;
+
+import ac.ed.lurg.country.Country;
+import ac.ed.lurg.types.Item;
+
+public interface YieldManager {
+
+	public double getRefYield(Country country, int year, Item commodity);
+	
+	public String getModelAndScenarioDescription();
+
+}