From 48609f7bcb2bbb4bb429fd2c608f2559821a0956 Mon Sep 17 00:00:00 2001
From: Peter Alexander <peter@blackhillock.co.uk>
Date: Mon, 15 Dec 2014 16:51:02 +0000
Subject: [PATCH] 'cookie' cutter the yields for a particular country

---
 src/ac/ed/lurg/ModelConfig.java               |  8 +-
 src/ac/ed/lurg/ModelContext.java              |  9 +-
 src/ac/ed/lurg/ModelMain.java                 | 64 +++++++++++----
 src/ac/ed/lurg/country/Country.java           | 33 ++++----
 src/ac/ed/lurg/country/CountryAgent.java      | 82 ++++++-------------
 .../ed/lurg/country/CountryAgentCreator.java  | 18 ++--
 .../CountryBoundaryItem.java}                 | 30 ++-----
 .../lurg/country/CountryBoundaryReader.java   | 30 +++++++
 src/ac/ed/lurg/country/CountryManager.java    | 70 ++++++++++++++++
 ...{GamsInput.java => GamsLocationInput.java} |  4 +-
 ...imiser.java => GamsLocationOptimiser.java} | 12 +--
 ...amsOutput.java => GamsLocationOutput.java} |  4 +-
 .../{GamsTest.java => GamsLocationTest.java}  |  8 +-
 .../country/gams/GamsRasterOptimiser.java     | 16 ++--
 .../lurg/country/gams/GamsRasterOutput.java   | 12 +--
 .../ed/lurg/country/gams/GamsRasterTest.java  |  4 +-
 src/ac/ed/lurg/demand/BaseConsumpManager.java |  3 +-
 src/ac/ed/lurg/demand/SspManager.java         |  3 +-
 src/ac/ed/lurg/utils/CollectionHelper.java    | 23 ++++++
 src/ac/ed/lurg/yield/YieldManager.java        | 76 -----------------
 src/ac/ed/lurg/yield/YieldRaster.java         | 16 +++-
 .../ed/lurg/yield/YieldResponseMapReader.java |  3 +-
 src/ac/ed/lurg/yield/YieldResponsesItem.java  |  2 +-
 src/ac/sac/raster/AbstractRasterReader.java   |  4 +-
 src/ac/sac/raster/RasterHeaderDetails.java    | 19 +++--
 src/ac/sac/raster/RasterSet.java              |  6 +-
 26 files changed, 300 insertions(+), 259 deletions(-)
 rename src/ac/ed/lurg/{yield/YieldKey.java => country/CountryBoundaryItem.java} (53%)
 create mode 100644 src/ac/ed/lurg/country/CountryBoundaryReader.java
 create mode 100644 src/ac/ed/lurg/country/CountryManager.java
 rename src/ac/ed/lurg/country/gams/{GamsInput.java => GamsLocationInput.java} (77%)
 rename src/ac/ed/lurg/country/gams/{GamsLandUseOptimiser.java => GamsLocationOptimiser.java} (96%)
 rename src/ac/ed/lurg/country/gams/{GamsOutput.java => GamsLocationOutput.java} (93%)
 rename src/ac/ed/lurg/country/gams/{GamsTest.java => GamsLocationTest.java} (94%)
 create mode 100644 src/ac/ed/lurg/utils/CollectionHelper.java
 delete mode 100644 src/ac/ed/lurg/yield/YieldManager.java

diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
index 8995025f..3f97841c 100644
--- a/src/ac/ed/lurg/ModelConfig.java
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -91,14 +91,14 @@ public class ModelConfig {
 	
 	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 String YIELD_DIR = DATA_DIR + File.separator + "yields";
-	public static final String REF_YIELD_FILE = DATA_DIR + File.separator + "ref_yields.csv";
 	public static final String BASELINE_CONSUMP_FILE = DATA_DIR + File.separator + "base_consump.csv";
-
 	
+	public static final String COUNTRY_BOUNDARY_FILE = DATA_DIR + File.separator + "country_boundaries.test.asc";
+	public static final String COUNTRY_CODES_FILE = DATA_DIR + File.separator + "country_codes3.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", 1);
 	public static final int BASE_YEAR = getIntProperty("BASE_YEAR", 2010);
diff --git a/src/ac/ed/lurg/ModelContext.java b/src/ac/ed/lurg/ModelContext.java
index 703a4030..4acc81ef 100644
--- a/src/ac/ed/lurg/ModelContext.java
+++ b/src/ac/ed/lurg/ModelContext.java
@@ -1,23 +1,16 @@
 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) {
+	public ModelContext(DemandManager demandManager) {
 		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
index d13f0450..cb9ddf54 100644
--- a/src/ac/ed/lurg/ModelMain.java
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -1,23 +1,32 @@
 package ac.ed.lurg;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
+import ac.ed.lurg.country.Country;
 import ac.ed.lurg.country.CountryAgent;
 import ac.ed.lurg.country.CountryAgentCreator;
+import ac.ed.lurg.country.CountryBoundaryItem;
+import ac.ed.lurg.country.CountryBoundaryReader;
 import ac.ed.lurg.demand.DemandManager;
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.types.ModelFitType;
 import ac.ed.lurg.types.YieldType;
-import ac.ed.lurg.yield.YieldManager;
-import ac.ed.lurg.yield.YieldResponsesItem;
+import ac.ed.lurg.utils.LogWriter;
+import ac.ed.lurg.yield.YieldRaster;
 import ac.ed.lurg.yield.YieldResponseMapReader;
+import ac.sac.raster.RasterKey;
 import ac.sac.raster.RasterSet;
 
 public class ModelMain {
 
 	private Collection<CountryAgent> countryAgents;
-	ModelContext modelContext;
+	private ModelContext modelContext;
+	private Map<Country, List<RasterKey>> countryToKeysMap;
 	
 	public static void main(String[] args)  {
 		ModelMain theModel = new ModelMain();
@@ -28,13 +37,33 @@ public class ModelMain {
 	/* setup models, reading inputs, etc. */
 	private void setup() {
 		DemandManager demandManager = new DemandManager(ModelFitType.LOGISTIC, "SSP2_v9_130325");
+
+		modelContext = new ModelContext(demandManager);
 		
-		YieldManager yieldManager = new YieldManager();
-		
-		modelContext = new ModelContext(demandManager, yieldManager);
-		
+		countryToKeysMap = getCountryToKeysMap();
+
 		countryAgents = new CountryAgentCreator().getAgents(modelContext);
 	}
+
+	private Map<Country, List<RasterKey>>  getCountryToKeysMap() {
+		CountryBoundaryReader countryReader = new CountryBoundaryReader();
+		RasterSet<CountryBoundaryItem> countryBoundaries = countryReader.getRasterDataFromFile(ModelConfig.COUNTRY_BOUNDARY_FILE);
+	
+		Map<Country, List<RasterKey>> countryToKeysMap = new HashMap<Country, List<RasterKey>>();
+
+		for (Map.Entry<RasterKey, CountryBoundaryItem> entry : countryBoundaries.entrySet()) {
+			List<RasterKey> keys = countryToKeysMap.get(entry.getValue().getCountry());
+			
+			if (keys == null) {
+				keys = new ArrayList<RasterKey>();
+				countryToKeysMap.put(entry.getValue().getCountry(), keys);
+			}
+			
+			keys.add(entry.getKey());
+ 		}
+		
+		return countryToKeysMap;
+	}
 	
 	/* run the model */
 	private void run() {
@@ -43,25 +72,32 @@ public class ModelMain {
 	}
 
 	private void doTimestep(int timestep) {
-		RasterSet<YieldResponsesItem> yieldSurfaces = getYieldSurfaces(timestep);
-		
+		YieldRaster yieldSurfaces = getYieldSurfaces(timestep);
+		LogWriter.println("Timestep " + timestep);
+
 		for (CountryAgent ca : countryAgents) {
-			ca.determineProduction(timestep);
+			LogWriter.println("Country " + ca.getCountry());
+			Collection<RasterKey> countryKeys = countryToKeysMap.get(ca.getCountry());
+			YieldRaster countryYieldSurfaces = yieldSurfaces.getSubsetRasterForKeys(countryKeys);
+			ca.determineProduction(timestep, countryYieldSurfaces);
 		}
 		
 		// examine global trade balance
 	}
 
-	private RasterSet<YieldResponsesItem> getYieldSurfaces(int timestep) {
-		String rootDir = ModelConfig.YIELD_DIR + File.separator + (timestep + ModelConfig.START_TIMESTEP) + File.separator;
-		RasterSet<YieldResponsesItem> yieldSurfaces = null;
+	private YieldRaster getYieldSurfaces(int timestep) {
+		String rootDir = ModelConfig.YIELD_DIR + File.separator + (timestep + ModelConfig.BASE_YEAR) + File.separator;
+		YieldRaster yieldSurfaces = null;
 		
 		for (CropType cropType : CropType.values()) {
+			String cropDir = rootDir + cropType.getGamsName() + File.separator;
+			
 			for (YieldType yieldType : YieldType.values()) {
 				YieldResponseMapReader yieldReader = new YieldResponseMapReader(yieldSurfaces, yieldType, cropType);
-				yieldSurfaces = yieldReader.getRasterDataFromFile(rootDir + yieldType.getFileName()); 
+				yieldSurfaces = (YieldRaster)yieldReader.getRasterDataFromFile(cropDir + yieldType.getFileName()); 
 			}
 		}
+		
 		return yieldSurfaces;
 	}
 }
diff --git a/src/ac/ed/lurg/country/Country.java b/src/ac/ed/lurg/country/Country.java
index 0337033e..36cddf64 100644
--- a/src/ac/ed/lurg/country/Country.java
+++ b/src/ac/ed/lurg/country/Country.java
@@ -1,33 +1,36 @@
 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 String iso3CharCode;
+	private int iso3NumCode;
 
-	private Country(String countryName) {
+	
+	public Country(String countryName, String iso3CharCode, int iso3NumCode) {
 		super();
 		this.countryName = countryName;
+		this.iso3CharCode = iso3CharCode;
+		this.iso3NumCode = iso3NumCode;
 	}
-	
-	
-	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 getIso3CharCode() {
+		return iso3CharCode;
+	}
+
+	public int getIso3NumCode() {
+		return iso3NumCode;
 	}
 
 	public String getCountryName() {
 		return countryName;
 	}
+	
+	@Override
+	public String toString() {
+		return countryName;
+	}
 
 	@Override
 	public int hashCode() {
diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
index 5291b92a..4c18334a 100644
--- a/src/ac/ed/lurg/country/CountryAgent.java
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -6,31 +6,29 @@ import java.util.Map;
 import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.ModelContext;
 import ac.ed.lurg.country.gams.GamsCountryInput;
-import ac.ed.lurg.country.gams.GamsInput;
-import ac.ed.lurg.country.gams.GamsLandUseOptimiser;
-import ac.ed.lurg.country.gams.GamsOutput;
+import ac.ed.lurg.country.gams.GamsRasterInput;
+import ac.ed.lurg.country.gams.GamsRasterOptimiser;
+import ac.ed.lurg.country.gams.GamsRasterOutput;
 import ac.ed.lurg.landuse.AreasItem;
 import ac.ed.lurg.types.CropType;
-import ac.ed.lurg.types.LandDataType;
 import ac.ed.lurg.utils.LogWriter;
-import ac.ed.lurg.yield.YieldResponsesItem;
+import ac.ed.lurg.yield.YieldRaster;
 
 public class CountryAgent {
 	
 	private ModelContext modelContext;
 	private Country country;
 	
-	private Map<Integer, AreasItem> cropAreasTimeseries = new HashMap<Integer, AreasItem>();
-//	private Map<Integer, LandCoverAreas> landUseAreasTimeseries = new HashMap<Integer, LandCoverAreas>();
+	private Map<Integer, GamsRasterOutput> resultsTimeseries = new HashMap<Integer, GamsRasterOutput>();
 
 	private int currentTimestep;
 	private Map<CropType, Double> currentProjectedDemand;
-	private Map<CropType, Double> currentRefYield;
+	private YieldRaster countryYieldSurfaces;
 		
 	public CountryAgent(ModelContext modelContext, Country country, AreasItem initialCropAreas) {
 		this.modelContext = modelContext;
 		this.country = country;
-		cropAreasTimeseries.put(0, initialCropAreas);
+//		cropAreasTimeseries.put(0, initialCropAreas);
 //		landUseAreasTimeseries.put(0, initialLandCover);
 	}
 	
@@ -38,70 +36,36 @@ public class CountryAgent {
 		return country;
 	}
 
-	public double getCropArea(int timestep, CropType item) {
-		AreasItem cd = cropAreasTimeseries.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) {
+	public void determineProduction(int timestep, YieldRaster countryYieldSurfaces) {
 		currentTimestep = timestep;
-		
-		int year = ModelConfig.BASE_YEAR + currentTimestep;
+		this.countryYieldSurfaces = countryYieldSurfaces;
 		
 		// get projected demand
+		int year = ModelConfig.BASE_YEAR + currentTimestep;
 		currentProjectedDemand = modelContext.getDemandManager().getDemand(country, year);
 		
-		// get ref yields
-		currentRefYield =  modelContext.getYieldManager().getRefYield(country, year);
-		
-		// optimise areas and intensity 
-		GamsCountryInput countryLevelInputs = new GamsCountryInput(getProjectedDemand(), getWorldInputEnergy(), getMaxNetImport(), getMinNetImport());
-		GamsInput gamsInput = new GamsInput(getYields(), getPreviousAreas(), countryLevelInputs);
-
-		GamsLandUseOptimiser opti = new GamsLandUseOptimiser(gamsInput);
-		LogWriter.println("Running " + country.getCountryName() + ", year " + year);
+		// optimize areas and intensity 
+		GamsRasterInput input = getGamsRasterInput();
+		GamsRasterOptimiser opti = new GamsRasterOptimiser(input);
+		LogWriter.println("Running " + country.getCountryName() + ", currentTimestep " + currentTimestep);
 		
-		GamsOutput result = opti.run();
-	//	cropAreasTimeseries.put(timestep, result);
+		GamsRasterOutput result = opti.run();
+		resultsTimeseries.put(timestep, result);
 	}
 
-	public Map<CropType, Double> getProjectedDemand() {
+	private Map<CropType, Double> getProjectedDemand() {
 		return currentProjectedDemand;
 	}
 
-	public Map<Integer, YieldResponsesItem> getYields() {
-	//	Map<Integer, Map<CropType, YieldResponse>> returnMap = new HashMap<Integer, Map<CropType, YieldResponse>>();
+	public GamsRasterInput getGamsRasterInput() {
 
-	//	for (int i= 1; i<=ModelConfig.NUM_LOCATIONS_PER_COUNTRY; i++)
-	//		returnMap.put(i, currentRefYield);
-
-		return null;  // this should be from LPJ data
-	}
-
-	public Map<Integer, AreasItem> getPreviousAreas() {		
-		AreasItem previousCropAreas = new AreasItem();
 		int previousTimestep = currentTimestep==0 ? 0 : currentTimestep-1;
-		AreasItem cd = cropAreasTimeseries.get(previousTimestep);
-		
-		double totalCropArea = 0;
-		for (CropType crop : CropType.getAllItems()) {
-			previousCropAreas.setCropArea(crop, cd.getCropArea(crop));
-			totalCropArea += cd.getCropArea(crop);
-		}
-		
-		previousCropAreas.setLandCoverArea(LandDataType.LAND, totalCropArea*2);
-		
-		Map<Integer, AreasItem> returnMap = new HashMap<Integer, AreasItem>();
-
-		for (int i= 1; i<=5; i++)
-			returnMap.put(i, previousCropAreas);
+		GamsRasterOutput prevOutput = resultsTimeseries.get(previousTimestep);
+				
+		GamsCountryInput countryLevelInputs = new GamsCountryInput(getProjectedDemand(), getWorldInputEnergy(), getMaxNetImport(), getMinNetImport());		
+		GamsRasterInput input = new GamsRasterInput(countryYieldSurfaces, prevOutput.getCropAreaRaster(), countryLevelInputs);
 
-		return returnMap;
+		return input;
 	}
 
 	public Map<CropType, Double> getWorldInputEnergy() {
diff --git a/src/ac/ed/lurg/country/CountryAgentCreator.java b/src/ac/ed/lurg/country/CountryAgentCreator.java
index 93a21f37..733fde29 100644
--- a/src/ac/ed/lurg/country/CountryAgentCreator.java
+++ b/src/ac/ed/lurg/country/CountryAgentCreator.java
@@ -9,21 +9,17 @@ import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.ModelContext;
 import ac.ed.lurg.landuse.AreasItem;
 import ac.ed.lurg.types.CropType;
-import ac.ed.lurg.types.LandDataType;
 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; 
-//	private static final int FERT_N_COL = 12; 
+	private static final int LAND_COL = 2; 
+	private static final int ARABLE_COL = 3; 
+	private static final int FOREST_COL = 5; 
+	private static final int PERM_CROP_COL = 6; 
+	private static final int PASTURE_COL = 7; 
 
 	public Collection<CountryAgent> getAgents(ModelContext modelContext) {
 		Collection<CountryAgent> countryAgents = new HashSet<CountryAgent>();
@@ -38,7 +34,7 @@ public class CountryAgentCreator {
 			while ((line=fitReader.readLine()) != null) {
 				String[] tokens = line.split(",");
 				
-				if (tokens.length < 13)
+				if (tokens.length < 11)
 					LogWriter.printlnError("Too few columns in " + filename + ", " + line);
 				
 				countryName = tokens[COUNTRY_COL];
@@ -66,7 +62,7 @@ public class CountryAgentCreator {
 				initialCropData.setCropArea(CropType.CEREALS, arableArea); // at the moment can substitute freely between arable crops so this is ok-ish
 				initialCropData.setCropArea(CropType.MEAT_OR_PASTURE, pastureArea);
 
-				CountryAgent ca = new CountryAgent(modelContext, Country.get(countryName), initialCropData);
+				CountryAgent ca = new CountryAgent(modelContext, CountryManager.getForName(countryName), initialCropData);
 						
 				countryAgents.add(ca);
 			} 
diff --git a/src/ac/ed/lurg/yield/YieldKey.java b/src/ac/ed/lurg/country/CountryBoundaryItem.java
similarity index 53%
rename from src/ac/ed/lurg/yield/YieldKey.java
rename to src/ac/ed/lurg/country/CountryBoundaryItem.java
index c744e722..822a854e 100644
--- a/src/ac/ed/lurg/yield/YieldKey.java
+++ b/src/ac/ed/lurg/country/CountryBoundaryItem.java
@@ -1,23 +1,17 @@
-package ac.ed.lurg.yield;
+package ac.ed.lurg.country;
 
-import ac.ed.lurg.country.Country;
-import ac.ed.lurg.types.CropType;
+import ac.sac.raster.RasterItem;
 
-class YieldKey {
-	private Country country;
-	private CropType item;
-	
-	YieldKey (Country country, CropType item) {
-		this.item = item;
-		this.country = country;
-	}
+public class CountryBoundaryItem implements RasterItem {
 
+	Country country;
+	
 	public Country getCountry() {
 		return country;
 	}
-
-	public CropType getItem() {
-		return item;
+	
+	public void setCountry(Country country) {
+		this.country = country;
 	}
 
 	@Override
@@ -25,7 +19,6 @@ class YieldKey {
 		final int prime = 31;
 		int result = 1;
 		result = prime * result + ((country == null) ? 0 : country.hashCode());
-		result = prime * result + ((item == null) ? 0 : item.hashCode());
 		return result;
 	}
 
@@ -37,17 +30,12 @@ class YieldKey {
 			return false;
 		if (getClass() != obj.getClass())
 			return false;
-		YieldKey other = (YieldKey) obj;
+		CountryBoundaryItem other = (CountryBoundaryItem) obj;
 		if (country == null) {
 			if (other.country != null)
 				return false;
 		} else if (!country.equals(other.country))
 			return false;
-		if (item == null) {
-			if (other.item != null)
-				return false;
-		} else if (!item.equals(other.item))
-			return false;
 		return true;
 	}
 }
diff --git a/src/ac/ed/lurg/country/CountryBoundaryReader.java b/src/ac/ed/lurg/country/CountryBoundaryReader.java
new file mode 100644
index 00000000..747a6e68
--- /dev/null
+++ b/src/ac/ed/lurg/country/CountryBoundaryReader.java
@@ -0,0 +1,30 @@
+package ac.ed.lurg.country;
+
+import ac.ed.lurg.utils.LogWriter;
+import ac.sac.raster.AbstractRasterReader;
+import ac.sac.raster.RasterHeaderDetails;
+import ac.sac.raster.RasterSet;
+
+public class CountryBoundaryReader extends AbstractRasterReader<CountryBoundaryItem> {	
+	
+	protected void createDataSet(RasterHeaderDetails header) {
+		if (DEBUG) LogWriter.println("Creating RasterDataset col:"+header.getNcolumns() + ", rows:" + header.getNrows());
+
+		if (dataset == null) {
+			dataset = new RasterSet<CountryBoundaryItem>(header) {
+				private static final long serialVersionUID = 8813968413893792926L;
+
+				protected CountryBoundaryItem createRasterData() {
+					return new CountryBoundaryItem();
+				}
+			};
+		}
+	}
+
+	@Override
+	public void setData(CountryBoundaryItem item, String token) {
+		int isoCode = Integer.parseInt(token);
+		Country c = CountryManager.getForCode(isoCode);
+		item.setCountry(c);
+	}
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/CountryManager.java b/src/ac/ed/lurg/country/CountryManager.java
new file mode 100644
index 00000000..7b75d35d
--- /dev/null
+++ b/src/ac/ed/lurg/country/CountryManager.java
@@ -0,0 +1,70 @@
+package ac.ed.lurg.country;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.utils.LogWriter;
+
+
+public class CountryManager {
+	private static final int NAME_COL = 0; 
+	private static final int CHAR_CODE_COL = 1; 
+	private static final int NUM_CODE_COL = 4; 
+
+	private final Map<String, Country> nameMap = new HashMap<String, Country>();
+	private final Map<Integer, Country> numCodeMap = new HashMap<Integer, Country>();
+	
+	private static CountryManager instance = null;
+
+	private static CountryManager getInstance() {
+		if(instance == null) {
+			instance = new CountryManager();
+		}
+		return instance;
+	}
+	
+	protected CountryManager() {
+		String filename = ModelConfig.COUNTRY_CODES_FILE;
+		try {
+			BufferedReader fitReader = new BufferedReader(new FileReader(filename)); 
+			String line, name, charCode;
+			int numCode;
+			fitReader.readLine(); // read header
+
+			while ((line=fitReader.readLine()) != null) {
+				String[] tokens = line.split(",");
+				
+				if (tokens.length < 4)
+					LogWriter.printlnError("Too few columns in " + filename + ", " + line);
+				
+				name = tokens[NAME_COL];
+				charCode = tokens[CHAR_CODE_COL];
+				numCode = Integer.parseInt(tokens[NUM_CODE_COL]);
+
+				Country c = new Country(name, charCode, numCode);
+				nameMap.put(name, c);
+				numCodeMap.put(numCode, c);
+			} 
+			fitReader.close(); 
+		
+		} catch (Exception e) {
+			LogWriter.printlnError("Failed in reading commodity demand fits");
+			e.printStackTrace();
+		}
+		LogWriter.println("Processed " + filename + ", create " + nameMap.size() + " country codes");
+
+	}
+	
+	public static Country getForName(String name) {
+		Country c = getInstance().nameMap.get(name);
+		return c;
+	}
+
+	public static Country getForCode(int isoCode) {
+		Country c = getInstance().numCodeMap.get(isoCode);
+		return c;
+	}
+}
diff --git a/src/ac/ed/lurg/country/gams/GamsInput.java b/src/ac/ed/lurg/country/gams/GamsLocationInput.java
similarity index 77%
rename from src/ac/ed/lurg/country/gams/GamsInput.java
rename to src/ac/ed/lurg/country/gams/GamsLocationInput.java
index 88d305cd..e3e5c95f 100644
--- a/src/ac/ed/lurg/country/gams/GamsInput.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationInput.java
@@ -5,13 +5,13 @@ import java.util.Map;
 import ac.ed.lurg.landuse.AreasItem;
 import ac.ed.lurg.yield.YieldResponsesItem;
 
-public class GamsInput {
+public class GamsLocationInput {
 	
 	private Map<Integer, ? extends YieldResponsesItem> yields;
 	private Map<Integer, ? extends AreasItem> previousAreas;
 	private GamsCountryInput countryInput;
 		
-	public GamsInput(Map<Integer, ? extends YieldResponsesItem> yields, Map<Integer, ? extends AreasItem> previousAreas, GamsCountryInput countryInput) {
+	public GamsLocationInput(Map<Integer, ? extends YieldResponsesItem> yields, Map<Integer, ? extends AreasItem> previousAreas, GamsCountryInput countryInput) {
 		super();
 		this.yields = yields;
 		this.previousAreas = previousAreas;
diff --git a/src/ac/ed/lurg/country/gams/GamsLandUseOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
similarity index 96%
rename from src/ac/ed/lurg/country/gams/GamsLandUseOptimiser.java
rename to src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
index d1801cf2..9e806dea 100644
--- a/src/ac/ed/lurg/country/gams/GamsLandUseOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
@@ -26,15 +26,15 @@ import com.gams.api.GAMSVariableRecord;
 import com.gams.api.GAMSWorkspace;
 import com.gams.api.GAMSWorkspaceInfo;
 
-public class GamsLandUseOptimiser {
+public class GamsLocationOptimiser {
 
-	private GamsInput inputData;
+	private GamsLocationInput inputData;
 
-	public GamsLandUseOptimiser(GamsInput inputData) {
+	public GamsLocationOptimiser(GamsLocationInput inputData) {
 		this.inputData = inputData;
 	}
 
-	public GamsOutput run() {
+	public GamsLocationOutput run() {
 
 		File workingDirectory = new File(ModelConfig.TEMP_DIR, "GamsTest");
 		workingDirectory.mkdir();
@@ -122,7 +122,7 @@ public class GamsLandUseOptimiser {
 		addScalar(inDB.addParameter("minFeedRate", 0, "minimum rate of feed for producing animal products"), inputData.getCountryInput().getMinFeedRate());
 	}
 
-	private GamsOutput handleResults(GAMSDatabase outDB) {
+	private GamsLocationOutput handleResults(GAMSDatabase outDB) {
 		int modelStatus = (int) outDB.getParameter("ms").findRecord().getValue();
 		System.out.println(
 				"\nModelstatus: " + GAMSGlobals.ModelStat.lookup( modelStatus ) +
@@ -186,7 +186,7 @@ public class GamsLandUseOptimiser {
 		LogWriter.println(String.format("\nTotal area= %.1f", totalArea));
 		//cleanup(ws.workingDirectory());
 		
-		GamsOutput results = new GamsOutput(modelStatus, intensities, cropAreas, feedAmounts, netImports);
+		GamsLocationOutput results = new GamsLocationOutput(modelStatus, intensities, cropAreas, feedAmounts, netImports);
 		return results ;
 	}
 
diff --git a/src/ac/ed/lurg/country/gams/GamsOutput.java b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java
similarity index 93%
rename from src/ac/ed/lurg/country/gams/GamsOutput.java
rename to src/ac/ed/lurg/country/gams/GamsLocationOutput.java
index 199b2ab8..2fb834a6 100644
--- a/src/ac/ed/lurg/country/gams/GamsOutput.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java
@@ -6,7 +6,7 @@ import ac.ed.lurg.landuse.AreasItem;
 import ac.ed.lurg.landuse.IntensitiesItem;
 import ac.ed.lurg.types.CropType;
 
-public class GamsOutput {
+public class GamsLocationOutput {
 	int status;
 	
 	Map<Integer, IntensitiesItem> intensities;  // data mapped from id (not raster)
@@ -14,7 +14,7 @@ public class GamsOutput {
 	Map<CropType, Double> feedAmounts;
 	Map<CropType, Double> netImports;
 	
-	public GamsOutput(int status, 
+	public GamsLocationOutput(int status, 
 			Map<Integer, IntensitiesItem> intensities, 
 			Map<Integer, AreasItem> cropAreas, 
 			Map<CropType, Double> feedAmounts,
diff --git a/src/ac/ed/lurg/country/gams/GamsTest.java b/src/ac/ed/lurg/country/gams/GamsLocationTest.java
similarity index 94%
rename from src/ac/ed/lurg/country/gams/GamsTest.java
rename to src/ac/ed/lurg/country/gams/GamsLocationTest.java
index e0464fa8..f0942978 100644
--- a/src/ac/ed/lurg/country/gams/GamsTest.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationTest.java
@@ -9,19 +9,19 @@ import ac.ed.lurg.types.LandDataType;
 import ac.ed.lurg.types.YieldType;
 import ac.ed.lurg.yield.YieldResponsesItem;
 
-public class GamsTest {
+public class GamsLocationTest {
 	public static final int NUM_LOCATIONS_PER_COUNTRY = 5;
 
 	public static void main(String[] args)  {
-		GamsTest aGamsTest = new GamsTest();
+		GamsLocationTest aGamsTest = new GamsLocationTest();
 		aGamsTest.run();
 	}
 	
 	private void run() {
 		GamsCountryInput countryLevelInputs = new GamsCountryInput(getProjectedDemand(), getWorldInputEnergy(), getMaxNetImport(), getMinNetImport());
-		GamsInput gamsInput = new GamsInput(getYields(), getPreviousArea(), countryLevelInputs);
+		GamsLocationInput gamsInput = new GamsLocationInput(getYields(), getPreviousArea(), countryLevelInputs);
 		
-		GamsLandUseOptimiser opti = new GamsLandUseOptimiser(gamsInput);		
+		GamsLocationOptimiser opti = new GamsLocationOptimiser(gamsInput);		
 		opti.run();
 	}
 
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
index 8c5ff657..8afa96fb 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
@@ -34,24 +34,24 @@ public class GamsRasterOptimiser {
 
 	public GamsRasterOutput run() {
 		// workout similar areas		
-		GamsInput gamsInput = convertFromRaster(rasterInputData);
+		GamsLocationInput gamsInput = convertFromRaster(rasterInputData);
 
 		// run optimizer
-		GamsLandUseOptimiser opti = new GamsLandUseOptimiser(gamsInput);		
-		GamsOutput gamsOutput = opti.run();
+		GamsLocationOptimiser opti = new GamsLocationOptimiser(gamsInput);		
+		GamsLocationOutput gamsOutput = opti.run();
 
 		// map results back to raster
 		return convertToRaster(gamsInput, gamsOutput);
 	}
 
-	private GamsRasterOutput convertToRaster(GamsInput gamsInput, GamsOutput gamsOutput) {		
+	private GamsRasterOutput convertToRaster(GamsLocationInput gamsInput, GamsLocationOutput gamsOutput) {		
 		RasterSet<AreasItem> newAreaRaster = allocAreas(gamsInput.getPreviousAreas(), gamsOutput);
 		RasterSet<IntensitiesItem> newIntensityRaster = allocIntensities(gamsOutput);
 		
 		return new GamsRasterOutput(gamsOutput.getStatus(), newIntensityRaster, newAreaRaster, gamsOutput.getFeedAmounts(), gamsOutput.getNetImports());
 	}
 
-	private RasterSet<AreasItem> allocAreas(Map<Integer, ? extends AreasItem> prevAreasAgg, GamsOutput gamsOutput) {
+	private RasterSet<AreasItem> allocAreas(Map<Integer, ? extends AreasItem> prevAreasAgg, GamsLocationOutput gamsOutput) {
 		RasterSet<AreasItem> newAreaRaster = new RasterSet<AreasItem>();
 		RasterSet<AreasItem> prevAreaRaster = rasterInputData.getPreviousAreas();
 		
@@ -148,7 +148,7 @@ public class GamsRasterOptimiser {
 		return netAddedCrop - prevForest - prevNatural;
 	}
 
-	private RasterSet<IntensitiesItem> allocIntensities(GamsOutput gamsOutput) {
+	private RasterSet<IntensitiesItem> allocIntensities(GamsLocationOutput gamsOutput) {
 
 		RasterSet<IntensitiesItem> newIntensityRaster = new RasterSet<IntensitiesItem>();
 
@@ -163,7 +163,7 @@ public class GamsRasterOptimiser {
 		return newIntensityRaster;
 	}
 
-	private GamsInput convertFromRaster(GamsRasterInput rasterInputData) {
+	private GamsLocationInput convertFromRaster(GamsRasterInput rasterInputData) {
 		// as a first attempt only going to look at pasture and cereal yields, assuming other yields will be correlated to one or the other.
 		YieldRaster yieldRaster = rasterInputData.getYields();
 		RasterSet<AreasItem> cropAreaRaster = rasterInputData.getPreviousAreas();
@@ -211,7 +211,7 @@ public class GamsRasterOptimiser {
 			}
 		}
 
-		return new GamsInput(aggregatedYield, aggregatedAreas, rasterInputData.getCountryInput());
+		return new GamsLocationInput(aggregatedYield, aggregatedAreas, rasterInputData.getCountryInput());
 	}
 
 	private Set<RasterKey> getMappingForLocation(Map<Integer, Set<RasterKey>> mapping, int id) {
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOutput.java b/src/ac/ed/lurg/country/gams/GamsRasterOutput.java
index ba94d2d5..c986777a 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterOutput.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterOutput.java
@@ -8,12 +8,13 @@ import ac.ed.lurg.types.CropType;
 import ac.sac.raster.RasterSet;
 
 public class GamsRasterOutput {
-	int status;
 	
-	RasterSet<IntensitiesItem> intensityRaster;
-	RasterSet<AreasItem> cropAreaRaster;
-	Map<CropType, Double> feedAmounts;
-	Map<CropType, Double> netImports;
+	private int status;
+	
+	private RasterSet<IntensitiesItem> intensityRaster;
+	private RasterSet<AreasItem> cropAreaRaster;
+	private Map<CropType, Double> feedAmounts;
+	private Map<CropType, Double> netImports;
 	
 	public GamsRasterOutput(int status, RasterSet<IntensitiesItem> intensityRaster, RasterSet<AreasItem> cropAreaRaster, 
 			Map<CropType, Double> feedAmounts, Map<CropType, Double> netImports) {
@@ -40,5 +41,4 @@ public class GamsRasterOutput {
 	public Map<CropType, Double> getNetImports() {
 		return netImports;
 	}
-
 }
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterTest.java b/src/ac/ed/lurg/country/gams/GamsRasterTest.java
index 136a8be5..aceb1e2b 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterTest.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterTest.java
@@ -1,12 +1,13 @@
 package ac.ed.lurg.country.gams;
 
 import ac.ed.lurg.landuse.AreasItem;
+import ac.ed.lurg.utils.LogWriter;
 import ac.ed.lurg.yield.YieldRaster;
 import ac.ed.lurg.yield.YieldResponsesItem;
 import ac.sac.raster.RasterKey;
 import ac.sac.raster.RasterSet;
 
-public class GamsRasterTest extends GamsTest {
+public class GamsRasterTest extends GamsLocationTest {
 	
 	public static void main(String[] args)  {
 		GamsRasterTest aGamsRasterTest = new GamsRasterTest();
@@ -19,6 +20,7 @@ public class GamsRasterTest extends GamsTest {
 		
 		GamsRasterOptimiser opti = new GamsRasterOptimiser(input);		
 		GamsRasterOutput output = opti.run();
+		LogWriter.println(output.toString());
 	}
 
 	private static final int X_RANGE = 3;
diff --git a/src/ac/ed/lurg/demand/BaseConsumpManager.java b/src/ac/ed/lurg/demand/BaseConsumpManager.java
index 91b8aa82..7931e013 100644
--- a/src/ac/ed/lurg/demand/BaseConsumpManager.java
+++ b/src/ac/ed/lurg/demand/BaseConsumpManager.java
@@ -7,6 +7,7 @@ import java.util.Map;
 
 import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.country.Country;
+import ac.ed.lurg.country.CountryManager;
 import ac.ed.lurg.types.CountryCropKey;
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.utils.LogWriter;
@@ -41,7 +42,7 @@ public class BaseConsumpManager {
 				cropName = tokens[CROP_COL];
 				cpc = Double.parseDouble(tokens[BASE_CPC_COL]);
 
-				baseConsumpMap.put(new CountryCropKey(Country.get(countryName), CropType.getForFaoName(cropName)), cpc);
+				baseConsumpMap.put(new CountryCropKey(CountryManager.getForName(countryName), CropType.getForFaoName(cropName)), cpc);
 			} 
 			fitReader.close(); 
 		
diff --git a/src/ac/ed/lurg/demand/SspManager.java b/src/ac/ed/lurg/demand/SspManager.java
index 46bc8ee9..e82b7c3a 100644
--- a/src/ac/ed/lurg/demand/SspManager.java
+++ b/src/ac/ed/lurg/demand/SspManager.java
@@ -7,6 +7,7 @@ import java.util.Map;
 
 import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.country.Country;
+import ac.ed.lurg.country.CountryManager;
 import ac.ed.lurg.utils.LogWriter;
 
 public class SspManager {
@@ -47,7 +48,7 @@ public class SspManager {
 					countryName = tokens[COUNTRY_COL];
 					gdp = Double.parseDouble(tokens[GDP_COL]);
 	
-					SspKey sspKey = new SspKey(scenario, year, Country.get(countryName));
+					SspKey sspKey = new SspKey(scenario, year, CountryManager.getForName(countryName));
 					SspData sspData = new SspData(population, gdp);
 					sspMap.put(sspKey, sspData);
 				} catch (Exception e) {
diff --git a/src/ac/ed/lurg/utils/CollectionHelper.java b/src/ac/ed/lurg/utils/CollectionHelper.java
new file mode 100644
index 00000000..44fd84ba
--- /dev/null
+++ b/src/ac/ed/lurg/utils/CollectionHelper.java
@@ -0,0 +1,23 @@
+package ac.ed.lurg.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CollectionHelper {
+
+	public static <K, V> Map<V, List<K>> invertMap(Map<K, V> map) {
+		
+		Map<V, List<K>> result = new HashMap<V, List<K>>();
+		
+		for (Map.Entry<K, V> entry : map.entrySet()) {
+			if (!result.containsKey(entry.getValue())) {
+				result.put(entry.getValue(), new ArrayList<K>());
+			}
+			result.get(entry.getValue()).add(entry.getKey());
+ 		}
+		
+		return result;
+	}
+}
diff --git a/src/ac/ed/lurg/yield/YieldManager.java b/src/ac/ed/lurg/yield/YieldManager.java
deleted file mode 100644
index 132e8e5f..00000000
--- a/src/ac/ed/lurg/yield/YieldManager.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package ac.ed.lurg.yield;
-
-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.types.CropType;
-import ac.ed.lurg.utils.LogWriter;
-
-public class YieldManager {
-
-	private Map<Country, Map<CropType, Double>> cache = new HashMap<Country, Map<CropType, Double>>();
-	
-	private static final int COUNTRY_COL = 0; 
-	private static final int ITEM_COL = 1;
-	private static final int REF_YIELD_COL = 2; 
-
-	public YieldManager () {
-		super();
-		readRefYields();
-	}
-	
-	private void readRefYields() {
-		String filename = ModelConfig.REF_YIELD_FILE;
-		try {
-			BufferedReader reader = new BufferedReader(new FileReader(filename)); 
-			String line, itemName, countryName;
-			double refYield;
-			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);
-				
-				try {
-					countryName = tokens[COUNTRY_COL];
-					itemName = tokens[ITEM_COL];
-					refYield = Double.parseDouble(tokens[REF_YIELD_COL]);
-	
-					Map<CropType, Double> countryMap = cache.get(Country.get(countryName));
-					
-					if (countryMap == null) {
-						countryMap = new HashMap<CropType, Double>();
-						cache.put(Country.get(countryName), countryMap);
-					}
-					countryMap.put(CropType.getForFaoName(itemName), refYield);
-					
-				} 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 " + cache.size() + " ref yield entries");
-	}
-
-	public Map<CropType, Double> getRefYield(Country country, int year) {
-		return cache.get(country);
-	}
-
-	public String getModelAndScenarioDescription() {
-		return "Estimated ref yields";
-	}
-
-	
-}
diff --git a/src/ac/ed/lurg/yield/YieldRaster.java b/src/ac/ed/lurg/yield/YieldRaster.java
index 6876bbf8..d982611f 100644
--- a/src/ac/ed/lurg/yield/YieldRaster.java
+++ b/src/ac/ed/lurg/yield/YieldRaster.java
@@ -1,7 +1,9 @@
 package ac.ed.lurg.yield;
 
-import ac.ed.lurg.country.Country;
+import java.util.Collection;
+
 import ac.sac.raster.RasterHeaderDetails;
+import ac.sac.raster.RasterKey;
 import ac.sac.raster.RasterSet;
 
 public class YieldRaster extends RasterSet<YieldResponsesItem> {
@@ -16,8 +18,14 @@ public class YieldRaster extends RasterSet<YieldResponsesItem> {
 		return new YieldResponsesItem();
 	}
 	
-	public YieldRaster getRasterForCountry(Country c) {
-		// TODO
-		return null;
+	// not very efficient, we could keep the mapping of country to area somewhere.
+	public YieldRaster getSubsetRasterForKeys(Collection<RasterKey> keys) {
+		YieldRaster subsetYieldRaster = new YieldRaster(this.getHeaderDetails());
+		
+		for (RasterKey key : keys) {
+			subsetYieldRaster.put(key, get(key));
+		}
+		
+		return subsetYieldRaster;
 	}
 }
diff --git a/src/ac/ed/lurg/yield/YieldResponseMapReader.java b/src/ac/ed/lurg/yield/YieldResponseMapReader.java
index 549975d9..94177331 100644
--- a/src/ac/ed/lurg/yield/YieldResponseMapReader.java
+++ b/src/ac/ed/lurg/yield/YieldResponseMapReader.java
@@ -5,14 +5,13 @@ import ac.ed.lurg.types.YieldType;
 import ac.ed.lurg.utils.LogWriter;
 import ac.sac.raster.AbstractRasterReader;
 import ac.sac.raster.RasterHeaderDetails;
-import ac.sac.raster.RasterSet;
 
 public class YieldResponseMapReader extends AbstractRasterReader<YieldResponsesItem> {	
 
 	private YieldType yieldType;
 	private CropType cropType;
 	
-	public YieldResponseMapReader (RasterSet<YieldResponsesItem> dataset, YieldType yieldType, CropType cropType) {
+	public YieldResponseMapReader (YieldRaster dataset, YieldType yieldType, CropType cropType) {
 		super(dataset);
 		this.yieldType = yieldType;
 		this.cropType = cropType;
diff --git a/src/ac/ed/lurg/yield/YieldResponsesItem.java b/src/ac/ed/lurg/yield/YieldResponsesItem.java
index db6a2776..216337bb 100644
--- a/src/ac/ed/lurg/yield/YieldResponsesItem.java
+++ b/src/ac/ed/lurg/yield/YieldResponsesItem.java
@@ -9,7 +9,7 @@ import ac.sac.raster.RasterItem;
 
 public class YieldResponsesItem implements RasterItem {
 	Map<CropType, YieldResponse> yieldResponses = new HashMap<CropType, YieldResponse>();
-
+	
 	public void setYield(YieldType type, CropType crop, double yield) {
 		getYieldResponseForCrop(crop).setYield(type, yield);
 	}
diff --git a/src/ac/sac/raster/AbstractRasterReader.java b/src/ac/sac/raster/AbstractRasterReader.java
index 284821a6..c9d5bea1 100755
--- a/src/ac/sac/raster/AbstractRasterReader.java
+++ b/src/ac/sac/raster/AbstractRasterReader.java
@@ -37,7 +37,7 @@ public abstract class AbstractRasterReader<D extends RasterItem> {
 		int nrows=0;
 		double xllCorner=0.0;
 		double yllCorner = 0.0;
-		int cellSize = 1000;
+		double cellSize = 1000;
 		String nodataString=null;
 		
 		for (int i=0; i<HEADER_LENGTH; i++) {
@@ -65,7 +65,7 @@ public abstract class AbstractRasterReader<D extends RasterItem> {
 				yllCorner = Double.parseDouble(tokens[HEADER_DATA_COL]);
 				break;
 			case CELL_SIZE:
-				cellSize =  Integer.parseInt(tokens[HEADER_DATA_COL]);
+				cellSize =  Double.parseDouble(tokens[HEADER_DATA_COL]);
 				break;
 			case NODATA_VALUE_ROW:
 				nodataString = tokens[HEADER_DATA_COL];
diff --git a/src/ac/sac/raster/RasterHeaderDetails.java b/src/ac/sac/raster/RasterHeaderDetails.java
index c45b0a52..31cd4c1a 100755
--- a/src/ac/sac/raster/RasterHeaderDetails.java
+++ b/src/ac/sac/raster/RasterHeaderDetails.java
@@ -5,10 +5,10 @@ public class RasterHeaderDetails {
 	private int nrows;
 	private double xllCorner;
 	private double yllCorner;
-	private int cellSize;
+	private double cellSize;
 	private String nodataString;
 	
-	public RasterHeaderDetails(int ncolumns, int nrows, double xllCorner, double yllCorner, int cellSize, String nodataString) {
+	public RasterHeaderDetails(int ncolumns, int nrows, double xllCorner, double yllCorner, double cellSize, String nodataString) {
 		super();
 		this.ncolumns = ncolumns;
 		this.nrows = nrows;
@@ -50,7 +50,7 @@ public class RasterHeaderDetails {
 		return yllCorner;
 	}
 
-	public int getCellSize() {
+	public double getCellSize() {
 		return cellSize;
 	}
 
@@ -58,16 +58,18 @@ public class RasterHeaderDetails {
 		return nodataString;
 	}
 
+	
 	@Override
 	public int hashCode() {
 		final int prime = 31;
 		int result = 1;
-		result = prime * result + cellSize;
+		long temp;
+		temp = Double.doubleToLongBits(cellSize);
+		result = prime * result + (int) (temp ^ (temp >>> 32));
 		result = prime * result + ncolumns;
 		result = prime * result
 				+ ((nodataString == null) ? 0 : nodataString.hashCode());
 		result = prime * result + nrows;
-		long temp;
 		temp = Double.doubleToLongBits(xllCorner);
 		result = prime * result + (int) (temp ^ (temp >>> 32));
 		temp = Double.doubleToLongBits(yllCorner);
@@ -81,10 +83,11 @@ public class RasterHeaderDetails {
 			return true;
 		if (obj == null)
 			return false;
-		if (!(obj instanceof RasterHeaderDetails))
+		if (getClass() != obj.getClass())
 			return false;
 		RasterHeaderDetails other = (RasterHeaderDetails) obj;
-		if (cellSize != other.cellSize)
+		if (Double.doubleToLongBits(cellSize) != Double
+				.doubleToLongBits(other.cellSize))
 			return false;
 		if (ncolumns != other.ncolumns)
 			return false;
@@ -103,7 +106,7 @@ public class RasterHeaderDetails {
 			return false;
 		return true;
 	}
-	
+
 	@Override
 	public String toString() {
 		return "ncolumns=" + ncolumns + 
diff --git a/src/ac/sac/raster/RasterSet.java b/src/ac/sac/raster/RasterSet.java
index 0114de81..9c5d994f 100755
--- a/src/ac/sac/raster/RasterSet.java
+++ b/src/ac/sac/raster/RasterSet.java
@@ -34,12 +34,12 @@ public class RasterSet<D extends RasterItem> extends HashMap<RasterKey, D> {
 			if (source.getCellSize() % header.getCellSize() != 0)
 				throw new RuntimeException("Cell sizes must be the same or souce an integer multiple of destination");
 			
-			int cellRatio = source.getCellSize() / header.getCellSize();
+			double cellRatio = source.getCellSize() / header.getCellSize();
 			
 			for (int x = 0; x<cellRatio; x++) {
 				for (int y = 0; y<cellRatio; y++) {
-					c = (col)*cellRatio + x + xOffset;
-					r = (row)*cellRatio + y + yOffset;
+					c = (int)(col*cellRatio) + x + xOffset;
+					r = (int)(row*cellRatio) + y + yOffset;
 
 					D d = get(c, r);
 					if (d != null)
-- 
GitLab