diff --git a/data/country_codes3.csv b/data/country_codes3.csv
deleted file mode 100644
index 5c5f90384582f1f3895595c64bd3cdadb7e7fc3b..0000000000000000000000000000000000000000
--- a/data/country_codes3.csv
+++ /dev/null
@@ -1 +0,0 @@
-Country,CountryCode,Region,IncomeGroup,numCode
Aruba,ABW,Latin America & Caribbean,High income: nonOECD,533
Afghanistan,AFG,South Asia,Low income,4
Angola,AGO,Sub-Saharan Africa,Upper middle income,24
Albania,ALB,Europe & Central Asia,Upper middle income,8
Andorra,AND,Europe & Central Asia,High income: nonOECD,20
United Arab Emirates,ARE,Middle East & North Africa,High income: nonOECD,784
Argentina,ARG,Latin America & Caribbean,Upper middle income,32
Armenia,ARM,Europe & Central Asia,Lower middle income,51
American Samoa,ASM,East Asia & Pacific,Upper middle income,16
Antigua and Barbuda,ATG,Latin America & Caribbean,High income: nonOECD,28
Australia,AUS,East Asia & Pacific,High income: OECD,36
Austria,AUT,Europe & Central Asia,High income: OECD,40
Azerbaijan,AZE,Europe & Central Asia,Upper middle income,31
Burundi,BDI,Sub-Saharan Africa,Low income,108
Belgium,BEL,Europe & Central Asia,High income: OECD,56
Benin,BEN,Sub-Saharan Africa,Low income,204
Burkina Faso,BFA,Sub-Saharan Africa,Low income,854
Bangladesh,BGD,South Asia,Low income,50
Bulgaria,BGR,Europe & Central Asia,Upper middle income,100
Bahrain,BHR,Middle East & North Africa,High income: nonOECD,48
Bahamas,BHS,Latin America & Caribbean,High income: nonOECD,44
Bosnia and Herzegovina,BIH,Europe & Central Asia,Upper middle income,70
Belarus,BLR,Europe & Central Asia,Upper middle income,112
Belize,BLZ,Latin America & Caribbean,Upper middle income,84
Bermuda,BMU,North America,High income: nonOECD,60
Bolivia (Plurinational State of),BOL,Latin America & Caribbean,Lower middle income,68
Brazil,BRA,Latin America & Caribbean,Upper middle income,76
Barbados,BRB,Latin America & Caribbean,High income: nonOECD,52
Brunei Darussalam,BRN,East Asia & Pacific,High income: nonOECD,96
Bhutan,BTN,South Asia,Lower middle income,64
Botswana,BWA,Sub-Saharan Africa,Upper middle income,72
Central African Republic,CAF,Sub-Saharan Africa,Low income,140
Canada,CAN,North America,High income: OECD,124
Switzerland,CHE,Europe & Central Asia,High income: OECD,756
Chile,CHL,Latin America & Caribbean,High income: OECD,152
China,CHN,East Asia & Pacific,Upper middle income,156
Cote d'Ivoire,CIV,Sub-Saharan Africa,Lower middle income,384
Cameroon,CMR,Sub-Saharan Africa,Lower middle income,120
Democratic Republic of the Congo,COD,Sub-Saharan Africa,Low income,180
Congo,COG,Sub-Saharan Africa,Lower middle income,178
Colombia,COL,Latin America & Caribbean,Upper middle income,170
Comoros,COM,Sub-Saharan Africa,Low income,174
Cabo Verde,CPV,Sub-Saharan Africa,Lower middle income,132
Costa Rica,CRI,Latin America & Caribbean,Upper middle income,188
Cuba,CUB,Latin America & Caribbean,Upper middle income,192
Cayman Islands,CYM,Latin America & Caribbean,High income: nonOECD,136
Cyprus,CYP,Europe & Central Asia,High income: nonOECD,196
Czech Republic,CZE,Europe & Central Asia,High income: OECD,203
Germany,DEU,Europe & Central Asia,High income: OECD,276
Djibouti,DJI,Middle East & North Africa,Lower middle income,262
Dominica,DMA,Latin America & Caribbean,Upper middle income,212
Denmark,DNK,Europe & Central Asia,High income: OECD,208
Dominican Republic,DOM,Latin America & Caribbean,Upper middle income,214
Algeria,DZA,Middle East & North Africa,Upper middle income,12
Ecuador,ECU,Latin America & Caribbean,Upper middle income,218
Egypt,EGY,Middle East & North Africa,Lower middle income,818
Eritrea,ERI,Sub-Saharan Africa,Low income,232
Spain,ESP,Europe & Central Asia,High income: OECD,724
Estonia,EST,Europe & Central Asia,High income: OECD,233
Ethiopia,ETH,Sub-Saharan Africa,Low income,231
Finland,FIN,Europe & Central Asia,High income: OECD,246
Fiji,FJI,East Asia & Pacific,Upper middle income,242
France,FRA,Europe & Central Asia,High income: OECD,250
Faroe Islands,FRO,Europe & Central Asia,High income: nonOECD,234
Micronesia (Federated States of),FSM,East Asia & Pacific,Lower middle income,583
Gabon,GAB,Sub-Saharan Africa,Upper middle income,266
United Kingdom,GBR,Europe & Central Asia,High income: OECD,826
Georgia,GEO,Europe & Central Asia,Lower middle income,268
Ghana,GHA,Sub-Saharan Africa,Lower middle income,288
Guinea,GIN,Sub-Saharan Africa,Low income,324
Gambia,GMB,Sub-Saharan Africa,Low income,270
Guinea-Bissau,GNB,Sub-Saharan Africa,Low income,624
Equatorial Guinea,GNQ,Sub-Saharan Africa,High income: nonOECD,226
Greece,GRC,Europe & Central Asia,High income: OECD,300
Grenada,GRD,Latin America & Caribbean,Upper middle income,308
Greenland,GRL,Europe & Central Asia,High income: nonOECD,304
Guatemala,GTM,Latin America & Caribbean,Lower middle income,320
Guam,GUM,East Asia & Pacific,High income: nonOECD,316
Guyana,GUY,Latin America & Caribbean,Lower middle income,328
Hong Kong,HKG,East Asia & Pacific,High income: nonOECD,344
Honduras,HND,Latin America & Caribbean,Lower middle income,340
Croatia,HRV,Europe & Central Asia,High income: nonOECD,191
Haiti,HTI,Latin America & Caribbean,Low income,332
Hungary,HUN,Europe & Central Asia,Upper middle income,348
Indonesia,IDN,East Asia & Pacific,Lower middle income,360
India,IND,South Asia,Lower middle income,356
Ireland,IRL,Europe & Central Asia,High income: OECD,372
Iran (Islamic Republic of),IRN,Middle East & North Africa,Upper middle income,364
Iraq,IRQ,Middle East & North Africa,Upper middle income,368
Iceland,ISL,Europe & Central Asia,High income: OECD,352
Israel,ISR,Middle East & North Africa,High income: OECD,376
Italy,ITA,Europe & Central Asia,High income: OECD,380
Jamaica,JAM,Latin America & Caribbean,Upper middle income,388
Jordan,JOR,Middle East & North Africa,Upper middle income,400
Japan,JPN,East Asia & Pacific,High income: OECD,392
Kazakhstan,KAZ,Europe & Central Asia,Upper middle income,398
Kenya,KEN,Sub-Saharan Africa,Low income,404
Kyrgyzstan,KGZ,Europe & Central Asia,Low income,417
Cambodia,KHM,East Asia & Pacific,Low income,116
Kiribati,KIR,East Asia & Pacific,Lower middle income,296
Saint Kitts and Nevis,KNA,Latin America & Caribbean,High income: nonOECD,659
Republic of Korea,KOR,East Asia & Pacific,High income: OECD,410
Kosovo,KSV,Europe & Central Asia,Lower middle income,410
Kuwait,KWT,Middle East & North Africa,High income: nonOECD,414
Lao People's Democratic Republic,LAO,East Asia & Pacific,Lower middle income,418
Lebanon,LBN,Middle East & North Africa,Upper middle income,422
Liberia,LBR,Sub-Saharan Africa,Low income,430
Libya,LBY,Middle East & North Africa,Upper middle income,434
Saint Lucia,LCA,Latin America & Caribbean,Upper middle income,662
Liechtenstein,LIE,Europe & Central Asia,High income: nonOECD,438
Sri Lanka,LKA,South Asia,Lower middle income,144
Lesotho,LSO,Sub-Saharan Africa,Lower middle income,426
Lithuania,LTU,Europe & Central Asia,High income: nonOECD,440
Luxembourg,LUX,Europe & Central Asia,High income: OECD,442
Latvia,LVA,Europe & Central Asia,High income: nonOECD,428
Macau,MAC,East Asia & Pacific,High income: nonOECD,446
Morocco,MAR,Middle East & North Africa,Lower middle income,504
Monaco,MCO,Europe & Central Asia,High income: nonOECD,492
Republic of Moldova,MDA,Europe & Central Asia,Lower middle income,498
Madagascar,MDG,Sub-Saharan Africa,Low income,450
Maldives,MDV,South Asia,Upper middle income,462
Mexico,MEX,Latin America & Caribbean,Upper middle income,484
Marshall Islands,MHL,East Asia & Pacific,Upper middle income,584
The former Yugoslav Republic of Macedonia,MKD,Europe & Central Asia,Upper middle income,807
Mali,MLI,Sub-Saharan Africa,Low income,466
Malta,MLT,Middle East & North Africa,High income: nonOECD,470
Myanmar,MMR,East Asia & Pacific,Low income,104
Montenegro,MNE,Europe & Central Asia,Upper middle income,499
Mongolia,MNG,East Asia & Pacific,Lower middle income,496
Northern Mariana Islands,MNP,East Asia & Pacific,High income: nonOECD,580
Mozambique,MOZ,Sub-Saharan Africa,Low income,508
Mauritania,MRT,Sub-Saharan Africa,Lower middle income,478
Mauritius,MUS,Sub-Saharan Africa,Upper middle income,480
Malawi,MWI,Sub-Saharan Africa,Low income,454
Malaysia,MYS,East Asia & Pacific,Upper middle income,458
Namibia,NAM,Sub-Saharan Africa,Upper middle income,516
New Caledonia,NCL,East Asia & Pacific,High income: OECD,540
Niger,NER,Sub-Saharan Africa,Low income,562
Nigeria,NGA,Sub-Saharan Africa,Lower middle income,566
Nicaragua,NIC,Latin America & Caribbean,Lower middle income,558
Netherlands,NLD,Europe & Central Asia,High income: OECD,528
Norway,NOR,Europe & Central Asia,High income: OECD,578
Nepal,NPL,South Asia,Low income,524
New Zealand,NZL,East Asia & Pacific,High income: OECD,554
Oman,OMN,Middle East & North Africa,High income: nonOECD,512
Pakistan,PAK,South Asia,Lower middle income,586
Panama,PAN,Latin America & Caribbean,Upper middle income,591
Peru,PER,Latin America & Caribbean,Upper middle income,604
Philippines,PHL,East Asia & Pacific,Lower middle income,608
Palau,PLW,East Asia & Pacific,Upper middle income,585
Papua New Guinea,PNG,East Asia & Pacific,Lower middle income,598
Poland,POL,Europe & Central Asia,High income: OECD,616
Puerto Rico,PRI,Latin America & Caribbean,High income: nonOECD,630
Democratic People's Republic of Korea,PRK,East Asia & Pacific,Low income,408
Portugal,PRT,Europe & Central Asia,High income: OECD,620
Paraguay,PRY,Latin America & Caribbean,Lower middle income,600
Occupied Palestinian Territory,PSE,Middle East & North Africa,Lower middle income,275
Pacific island small states,PSS,,,275
French Polynesia,PYF,East Asia & Pacific,High income: nonOECD,258
Qatar,QAT,Middle East & North Africa,High income: nonOECD,634
Romania,ROU,Europe & Central Asia,Upper middle income,642
Russian Federation,RUS,Europe & Central Asia,High income: nonOECD,643
Rwanda,RWA,Sub-Saharan Africa,Low income,646
Saudi Arabia,SAU,Middle East & North Africa,High income: nonOECD,682
Sudan,SDN,Sub-Saharan Africa,Lower middle income,729
Senegal,SEN,Sub-Saharan Africa,Lower middle income,686
Singapore,SGP,East Asia & Pacific,High income: nonOECD,702
Solomon Islands,SLB,East Asia & Pacific,Lower middle income,90
Sierra Leone,SLE,Sub-Saharan Africa,Low income,694
El Salvador,SLV,Latin America & Caribbean,Lower middle income,222
San Marino,SMR,Europe & Central Asia,High income: nonOECD,674
Somalia,SOM,Sub-Saharan Africa,Low income,706
Serbia,SRB,Europe & Central Asia,Upper middle income,688
Sao Tome and Principe,STP,Sub-Saharan Africa,Lower middle income,678
Suriname,SUR,Latin America & Caribbean,Upper middle income,740
Slovakia,SVK,Europe & Central Asia,High income: OECD,703
Slovenia,SVN,Europe & Central Asia,High income: OECD,705
Sweden,SWE,Europe & Central Asia,High income: OECD,752
Swaziland,SWZ,Sub-Saharan Africa,Lower middle income,748
Seychelles,SYC,Sub-Saharan Africa,Upper middle income,690
Syrian Arab Republic,SYR,Middle East & North Africa,Lower middle income,760
Turks and Caicos Islands,TCA,Latin America & Caribbean,High income: nonOECD,796
Chad,TCD,Sub-Saharan Africa,Low income,148
Togo,TGO,Sub-Saharan Africa,Low income,768
Thailand,THA,East Asia & Pacific,Upper middle income,764
Tajikistan,TJK,Europe & Central Asia,Low income,762
Turkmenistan,TKM,Europe & Central Asia,Upper middle income,795
Timor-Leste,TLS,East Asia & Pacific,Lower middle income,626
Tonga,TON,East Asia & Pacific,Upper middle income,776
Trinidad and Tobago,TTO,Latin America & Caribbean,High income: nonOECD,780
Tunisia,TUN,Middle East & North Africa,Upper middle income,788
Turkey,TUR,Europe & Central Asia,Upper middle income,792
Tuvalu,TUV,East Asia & Pacific,Upper middle income,798
United Republic of Tanzania,TZA,Sub-Saharan Africa,Low income,834
Uganda,UGA,Sub-Saharan Africa,Low income,800
Ukraine,UKR,Europe & Central Asia,Lower middle income,804
Uruguay,URY,Latin America & Caribbean,High income: nonOECD,858
United States of America,USA,North America,High income: OECD,840
Uzbekistan,UZB,Europe & Central Asia,Lower middle income,860
Saint Vincent and the Grenadines,VCT,Latin America & Caribbean,Upper middle income,670
Venezuela (Bolivarian Republic of),VEN,Latin America & Caribbean,Upper middle income,862
United States Virgin Islands,VIR,Latin America & Caribbean,High income: nonOECD,850
Viet Nam,VNM,East Asia & Pacific,Lower middle income,704
Vanuatu,VUT,East Asia & Pacific,Lower middle income,548
Samoa,WSM,East Asia & Pacific,Lower middle income,882
Yemen,YEM,Middle East & North Africa,Lower middle income,887
South Africa,ZAF,Sub-Saharan Africa,Upper middle income,710
Zambia,ZMB,Sub-Saharan Africa,Lower middle income,894
Zimbabwe,ZWE,Sub-Saharan Africa,Low income,716
Czechoslovakia,CZEF,Europe & Central Asia,High income: OECD,203
Ethiopia PDR,ETHF,Sub-Saharan Africa,Low income,231
Sudan (former),SDNF,Sub-Saharan Africa,Lower middle income,729
Western Sahara,WSAF,Sub-Saharan Africa,Lower middle income,876
Yugoslav SFR,YUGF,Europe & Central Asia,High income: OECD,887
Belgium-Luxembourg,BELF,Europe & Central Asia,High income: OECD,56
Serbia and Montenegro,SRBF,Europe & Central Asia,Upper middle income,688
Falkland Islands (Malvinas),FALF,Latin America & Caribbean,High income: nonOECD,231
South Sudan,SSUF,Sub-Saharan Africa,Lower middle income,728
USSR,USSR,Europe & Central Asia,High income: nonOECD,999
\ No newline at end of file
diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java
index de86cf01f466c747989be0dd55f5a3db03c6d693..8e5a01a5c9c1455c5db2103dfd3a1003e7aebdb9 100644
--- a/src/ac/ed/lurg/InternationalMarket.java
+++ b/src/ac/ed/lurg/InternationalMarket.java
@@ -7,7 +7,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import ac.ed.lurg.country.CountryAgent;
+import ac.ed.lurg.country.AbstractCountryAgent;
 import ac.ed.lurg.country.GlobalPrice;
 import ac.ed.lurg.country.StockReader;
 import ac.ed.lurg.landuse.CropUsageData;
@@ -47,10 +47,10 @@ public class InternationalMarket {
 		return initialStockLevels;
 	}
 
-	void determineInternationalTrade(Collection<CountryAgent> countryAgents, double gen2EcDDemand, Timestep timestep) {
+	void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double gen2EcDDemand, Timestep timestep) {
 		CropToDoubleMap totalImportCommodities = new CropToDoubleMap();
 		CropToDoubleMap totalExportCommodities = new CropToDoubleMap();
-		for (CountryAgent ca : countryAgents) {
+		for (AbstractCountryAgent ca : countryAgents) {
 
 			// Get values for world input costs
 			Map<CropType, CropUsageData> cropUsage = ca.getCropUsageData();
diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
index b40d449ba23cd12713225faee3f5ea247c4db75c..550878a61ad93dc6dc5b590546f63ed30d2967f6 100644
--- a/src/ac/ed/lurg/ModelConfig.java
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -272,7 +272,8 @@ public class ModelConfig {
 	public static final double TOTAL_MAX_CEREAL_CHANGE = getDoubleProperty("TOTAL_MAX_CEREAL_CHANGE", 0.5);
 	public static final double ANNUAL_MAX_CEREAL_CHANGE = getDoubleProperty("ANNUAL_MAX_CEREAL_CHANGE", 0.0);
 	public static final boolean LIMIT_CEREAL_FRACTION = getBooleanProperty("LIMIT_CEREAL_FRACTION", true);
-
+	public static final int CEREAL_CHANGE_START_STEP = getIntProperty("CEREAL_CHANGE_START_STEP", 0);
+	
 	public static final double PASTURE_HARVEST_FRACTION = getDoubleProperty("PASTURE_HARVEST_FRACTION", 0.5);
 	public static final double ANIMAL_FEED_FROM_OTHER_SOURCES_RATE = getDoubleProperty("ANIMAL_FEED_FROM_OTHER_SOURCES_RATE", 0.127);  // animal nutrition coming from sources other crops modelled
 	public static final double MEAT_EFFICIENCY = getDoubleProperty("MEAT_EFFICIENCY", 1.0);  // 'meat' is includes feed conversion ratio already, this is tech. change or similar
@@ -369,4 +370,7 @@ public class ModelConfig {
 	public static final int FORCE_PROTECTED_AREAS_END_YEAR  = getIntProperty("FORCE_PROTECTED_AREAS_END_YEAR", 2050);
 	
 	public static final double US_EXPORT_TARRIFF = getDoubleProperty("US_EXPORT_TARRIFF",1.0);
+	
+	public static final boolean USE_CRAFTY_COUNTRIES = getBooleanProperty("USE_CRAFTY_COUNTRIES", false);
+	public static final String CRAFTY_PRODUCTION_DIR = getProperty("CRAFTY_PRODUCTION_DIR", OUTPUT_DIR + File.separator + "crafty");
 }
diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java
index 0e3570057bf695d4b9e1bb9565f8515257617bf4..ceb4c58d404b4c858bd4f8b4ac5801797273572e 100644
--- a/src/ac/ed/lurg/ModelMain.java
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -8,25 +8,19 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import ac.ed.lurg.country.AbstractCountryAgent;
 import ac.ed.lurg.country.AnimalRateManager;
 import ac.ed.lurg.country.CompositeCountry;
 import ac.ed.lurg.country.CompositeCountryManager;
-import ac.ed.lurg.country.CountryAgent;
+import ac.ed.lurg.country.CountryAgentManager;
 import ac.ed.lurg.country.CountryBoundaryRaster;
 import ac.ed.lurg.country.CountryBoundaryReader;
 import ac.ed.lurg.country.CountryPrice;
 import ac.ed.lurg.country.GlobalPrice;
-import ac.ed.lurg.country.SubsidyRateManager;
-import ac.ed.lurg.country.TradeManager;
 import ac.ed.lurg.demand.AbstractDemandManager;
 import ac.ed.lurg.demand.BaseConsumpManager;
 import ac.ed.lurg.demand.DemandManagerFromFile;
@@ -63,12 +57,10 @@ import ac.sac.raster.RasterSet;
 
 public class ModelMain {
 
-	private Collection<CountryAgent> countryAgents;
+	private CountryAgentManager countryAgents;
 	private CountryBoundaryRaster countryBoundaryRaster;
 	private AbstractDemandManager demandManager;
-	private SubsidyRateManager subsidyRateManager;
 	private AnimalRateManager animalRateManager;
-	private TradeManager tradeManager;
 	private CompositeCountryManager compositeCountryManager;
 	LPJYieldResponseMapReader lpjYieldReader;
 	private RasterHeaderDetails desiredProjection;
@@ -91,7 +83,6 @@ public class ModelMain {
 
 		BaseConsumpManager baseConsumpManager = new BaseConsumpManager();
 		compositeCountryManager = new CompositeCountryManager(baseConsumpManager);
-		subsidyRateManager = new SubsidyRateManager(compositeCountryManager);
 		lpjYieldReader = new LPJYieldResponseMapReader(desiredProjection);
 		animalRateManager = new AnimalRateManager(compositeCountryManager);
 		
@@ -102,14 +93,13 @@ public class ModelMain {
 		
 		if (ModelConfig.SHOCKS_POSSIBLE) ModelConfig.readInShocksFile();
 			
-		tradeManager = new TradeManager(compositeCountryManager);
 		currentIrrigationData = getFixedIrrigationData();
 		countryBoundaryRaster = getCountryBoundaryRaster();
 		clusterIdRaster = ModelConfig.GENERATE_NEW_YIELD_CLUSTERS ? new RasterSet<IntegerRasterItem>(desiredProjection) : getClusterRaster();
 
-		countryAgents = createCountryAgents(compositeCountryManager.getAll());
 		globalLandUseRaster = new RasterSet<LandUseItem>(desiredProjection);
 		internationalMarket = new InternationalMarket();
+		createCountryAgents(compositeCountryManager.getAll());
 	}
 
 	/* run the model */
@@ -124,9 +114,9 @@ public class ModelMain {
 			}
 		}
 	}
-
+	
 	private void doTimestep(Timestep timestep) {
-		LogWriter.println(timestep.toString());
+		LogWriter.println("Timestep: " + timestep.toString());
 
 		YieldRaster yieldSurfaces = getYieldSurfaces(timestep); // this will wait for the marker file from LPJ if configured to do so
 		getUpdateIrrigationData(timestep, yieldSurfaces); // updating currentIrrigationData
@@ -134,48 +124,10 @@ public class ModelMain {
 		double previousGen2EcDDemand = (timestep.isInitialTimestep() || ModelConfig.IS_CALIBRATION_RUN ) ? 0: demandManager.getSecondGenBioenergyDemand(timestep.getPreviousTimestep());
 		double gen2EcDDemand = demandManager.getSecondGenBioenergyDemand(ModelConfig.IS_CALIBRATION_RUN ? new Timestep(1) : timestep);
 		double gen2Increase = (gen2EcDDemand>previousGen2EcDDemand) ? gen2EcDDemand - previousGen2EcDDemand : 0.0;
-
-		for (CountryAgent ca : countryAgents) {
-
-			LogWriter.println("Country " + ca.getCountry());
-			Collection<RasterKey> countryKeys = countryBoundaryRaster.getKeysFor(ca.getCountry());
-			YieldRaster countryYieldSurfaces = yieldSurfaces.createSubsetForKeys(countryKeys);
-			RasterSet<IrrigationItem> irrigData = currentIrrigationData.createSubsetForKeys(countryKeys);
-			
-			// do the optimization
-			try {
-				ca.determineProduction(timestep, countryYieldSurfaces, irrigData, internationalMarket.getWorldPrices(), gen2Increase);
-			} catch (Exception e) {
-				LogWriter.printlnError("Exception processing " + ca.getCountry() + " will continue with other countries");
-				LogWriter.print(e);
-				continue;
-			}
-
-			// some hacky code for debug purposes that keeps each gams gdx file
-			// for one country
-			if (ModelConfig.GAMS_COUNTRY_TO_SAVE != null && ca.getCountry().getName().equals(ModelConfig.GAMS_COUNTRY_TO_SAVE)) {
-				try {
-					Files.copy(
-							FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + "_gams_java_gdb1.gdx"),
-							FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + ModelConfig.GAMS_COUNTRY_TO_SAVE + timestep.getYear() + ".gdx"),
-							StandardCopyOption.REPLACE_EXISTING);
-				} catch (IOException e) {
-					LogWriter.print(e);
-				}
-			}
-
-			// update global rasters
-			globalLandUseRaster.putAll(ca.getLandUses());
-
-			// if first timestep and calibration get the clustering info, which
-			// doesn't change through time
-			if (ModelConfig.GENERATE_NEW_YIELD_CLUSTERS && timestep.isInitialTimestep())
-				clusterIdRaster.putAll(ca.getYieldClusters());
-			
-			if (ca.getCountry().getName().equals("United Kingdom"))	 writeUKLandCoverFile(timestep, ca);			
-		}
 		
-		internationalMarket.determineInternationalTrade(countryAgents, gen2EcDDemand, timestep);
+		countryAgents.processTimestepForAll(timestep, yieldSurfaces, currentIrrigationData, gen2Increase);
+		
+		internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, timestep);
 
 		// output results
 		outputTimestepResults(timestep, globalLandUseRaster);
@@ -249,7 +201,7 @@ public class ModelMain {
 					stockChange = priceQuantity.getStockChange();
 				}
 				
-				for (CountryAgent ca : countryAgents) {
+				for (AbstractCountryAgent ca : countryAgents.getAll()) {
 					Map<CropType, CropUsageData> allCropUsage = ca.getCropUsageData();
 					CropUsageData cropUsage = allCropUsage.get(crop);
 					if (cropUsage != null) {
@@ -298,7 +250,7 @@ public class ModelMain {
 			for (CommodityType comm : CommodityType.getAllItems()) {
 				double demandAmount = 0;
 
-				for (CountryAgent country : countryAgents) {
+				for (AbstractCountryAgent country : countryAgents.getAll()) {
 					Double d = country.getCurrentProjectedDemand().get(comm);
 					if (d != null) {
 						demandAmount += d.doubleValue();
@@ -325,7 +277,7 @@ public class ModelMain {
 			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.DOMESTIC_OUTPUT_FILE, sbHeadings.toString());
 
 			for (CropType crop : CropType.getAllItems()) {
-				for (CountryAgent country : countryAgents) {
+				for (AbstractCountryAgent country : countryAgents.getAll()) {
 					
 					Map<CropType, CropUsageData> cropUsageAllCrops = country.getCropUsageData();
 					CropUsageData cropUsage = cropUsageAllCrops.get(crop);
@@ -367,44 +319,13 @@ public class ModelMain {
 		}
 	}
 
-	private void writeUKLandCoverFile(Timestep timestep, CountryAgent ca){
-
-		try {
-			StringBuffer sbHeadings = new StringBuffer("Year,Cropland,Pasture,ManForest,UnmanForest,Natural,AbPasture,EnergyCrop,FertCrop,IrrigCrop");
-			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.UK_LAND_COVER_OUTPUT_FILE, sbHeadings.toString());
-
-			StringBuffer sbData = new StringBuffer();
-			Collection<RasterKey> countryKeys = countryBoundaryRaster.getKeysFor(ca.getCountry());
-			RasterSet<LandUseItem> ukLandUse = globalLandUseRaster.createSubsetForKeys(countryKeys);
-
-			sbData.append(String.format("%d,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f", timestep.getYear(),
-					LandUseItem.getTotalLandCover(ukLandUse.values(), LandCoverType.CROPLAND),
-					LandUseItem.getTotalLandCover(ukLandUse.values(), LandCoverType.PASTURE),
-					LandUseItem.getTotalLandCover(ukLandUse.values(), LandCoverType.MANAGED_FOREST),
-					LandUseItem.getTotalLandCover(ukLandUse.values(), LandCoverType.UNMANAGED_FOREST),
-					LandUseItem.getTotalLandCover(ukLandUse.values(), LandCoverType.OTHER_NATURAL),
-					LandUseItem.getAbandonedPasture(ukLandUse.values()))
-					);
-
-			sbData.append(String.format(",%.1f", LandUseItem.getTotalCropArea(ukLandUse.values(), CropType.ENERGY_CROPS)));
-			sbData.append(String.format(",%.1f", LandUseItem.getFertiliserTotal(ukLandUse.values(), CropType.getCropsLessPasture()) / 1000));
-			sbData.append(String.format(",%.1f", LandUseItem.getIrrigationTotal(ukLandUse.values(), CropType.getCropsLessPasture())));
-
-			outputFile.write(sbData.toString());
-			outputFile.newLine();
-			outputFile.close();
-		} catch (IOException e) {
-			LogWriter.print(e);
-		}
-	}
-
 	private void writeCountryDemandFile(Timestep timestep){
 
 		try {
 			StringBuffer sbHeadings = new StringBuffer("Year, Country, Commodity, Demand");
 			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.COUNTRY_DEMAND_FILE, sbHeadings.toString());
 
-			for (CountryAgent country : countryAgents) {
+			for (AbstractCountryAgent country : countryAgents.getAll()) {
 				for (CommodityType commodity : CommodityType.getAllItems()) {
 
 					double  demand =  country.getCurrentProjectedDemand().get(commodity);
@@ -429,10 +350,13 @@ public class ModelMain {
 			StringBuffer sbHeadings = new StringBuffer("Year,Country,FAOItem,Heads(M)");
 			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.ANIMAL_NUMBERS_OUTPUT_FILE, sbHeadings.toString());
 		
-			for (CountryAgent country : countryAgents) {
+			for (AbstractCountryAgent country : countryAgents.getAll()) {
 				Map<CropType, CropUsageData> cropUsageAllCrops = country.getCropUsageData();
 				for (CropType crop : CropType.getMeatTypes()) {
-					double prod = cropUsageAllCrops.get(crop).getProduction();
+					CropUsageData cropusage = cropUsageAllCrops.get(crop);
+					if (cropusage == null)
+						continue;
+					double prod = cropusage.getProduction();
 					
 					Map<String, Double> animalRates = animalRateManager.getAnimalRates(country.getCountry(), crop);
 					for (Entry<String, Double> entry : animalRates.entrySet()) {
@@ -492,7 +416,7 @@ public class ModelMain {
 			serializeLandUse(landUseRaster);
 
 		if (timestep.isInitialTimestep() && ModelConfig.GENERATE_NEW_YIELD_CLUSTERS)
-				outputClusters(clusterIdRaster);
+			outputClusters(clusterIdRaster);
 
 		// Output LandUses to tabular file, for analysis (perhaps)
 		LogWriter.println("Outputing land uses Year: " + timestep.getYear());
@@ -552,43 +476,14 @@ public class ModelMain {
 		return countryBoundaries;
 	}
 
-	public Collection<CountryAgent> createCountryAgents(Collection<CompositeCountry> countryGrouping) {
-		Collection<CountryAgent> countryAgents = new HashSet<CountryAgent>();
+	public void createCountryAgents(Collection<CompositeCountry> countryGrouping) {
+		countryAgents = new CountryAgentManager(compositeCountryManager, demandManager, countryBoundaryRaster, internationalMarket, clusterIdRaster, globalLandUseRaster);
 		Map<CompositeCountry, Map<CropType, CropUsageData>> cropUsageDataMap = new CropUsageReader(compositeCountryManager).getCommodityData();
 		RasterSet<LandUseItem> initLU = getInitialLandUse();
 
 		for (CompositeCountry cc : countryGrouping) {
-
-			// DEBUG code
-			if (ModelConfig.DEBUG_LIMIT_COUNTRIES) {
-				if (!(cc.getName().equals(ModelConfig.DEBUG_COUNTRY_NAME)))
-					continue;
-			}
-
-			List<RasterKey> keys = countryBoundaryRaster.getKeysFor(cc);
-			Map<CropType, CropUsageData> countryCommodityData = cropUsageDataMap.get(cc);
-			Map<CropType, Double> countryTradeBarriers = tradeManager.getTradeBarriers(cc);
-
-			if (countryCommodityData == null) {
-				LogWriter.printlnError("No commodities data for " + cc + ", so skipping");
-			} else {
-				RasterSet<LandUseItem> initCountryLandUse = initLU.createSubsetForKeys(keys);
-				RasterSet<IntegerRasterItem> yieldClusters = ModelConfig.GENERATE_NEW_YIELD_CLUSTERS ? null : clusterIdRaster.createSubsetForKeys(keys);
-
-				if (initCountryLandUse.size() == 0) {
-					LogWriter.printlnError("No initial land use for " + cc + ", so skipping");
-					continue;
-				}
-				
-				Map<CropType, Double> subsidyRates = subsidyRateManager.getSubsidyRates(cc);
-
-				CountryAgent ca = new CountryAgent(demandManager, cc, initCountryLandUse, countryCommodityData, countryTradeBarriers, yieldClusters, subsidyRates);
-				countryAgents.add(ca);
-				LogWriter.println("Creating country agent for: " + cc);
-			}
+			countryAgents.addForCountry(cc, cropUsageDataMap, initLU);
 		}
-
-		return countryAgents;
 	}
 
 	private RasterSet<LandUseItem> getInitialLandUse() {
diff --git a/src/ac/ed/lurg/country/AbstractCountryAgent.java b/src/ac/ed/lurg/country/AbstractCountryAgent.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6480482084527222d5967d56418deb6d522962d
--- /dev/null
+++ b/src/ac/ed/lurg/country/AbstractCountryAgent.java
@@ -0,0 +1,131 @@
+package ac.ed.lurg.country;
+
+import java.util.HashMap;
+import java.util.Map;
+
+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.types.CommodityType;
+import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.utils.LogWriter;
+
+public abstract class AbstractCountryAgent {
+
+	protected AbstractDemandManager demandManager;
+	protected CompositeCountry country;
+	protected Map<CropType, Double> tradeBarriers;
+	protected Map<CommodityType, Double> currentProjectedDemand;
+	private Map<CropType, CountryPrice> currentCountryPrices;
+
+	public AbstractCountryAgent(AbstractDemandManager demandManager,CompositeCountry country, Map<CropType, Double> tradeBarriers) {
+
+		this.demandManager = demandManager;
+		this.country = country;
+		this.tradeBarriers = tradeBarriers;
+	}
+
+	public CompositeCountry getCountry() {
+		return country;
+	}
+	
+	protected Map<CropType, CountryPrice> calculateCountryPrices(Map<CropType, GlobalPrice> worldPrices, Timestep timestep){
+		Map<CropType, CountryPrice> countryPrices = new HashMap <CropType, CountryPrice>();
+
+		for (CropType c : CropType.getImportedTypes()) {
+			GlobalPrice worldPrice = worldPrices.get(c);
+			CountryPrice prices = new CountryPrice(worldPrice.getCountryImportPrice(tradeBarriers.get(c), timestep), worldPrice.getExportPrice());
+			countryPrices.put(c, prices);
+		}
+
+		currentCountryPrices = countryPrices;
+		return currentCountryPrices;
+	}
+	
+	protected Map<CommodityType, Double> calculateDemand(Timestep timestep) {
+		currentProjectedDemand = demandManager.getDemand(country, timestep.getYear());
+		return currentProjectedDemand;
+	}
+	
+	public Map<CommodityType, Double> getCurrentProjectedDemand() {
+		return currentProjectedDemand;
+	}
+	
+	public Map<CropType, CountryPrice> getCurrentCountryPrices() {
+		return currentCountryPrices;
+	}
+	
+	protected Map<CropType, Double> getMinCerealFraction(Timestep timestep) {
+		Map<CropType, Double> minCerealFraction = new HashMap<CropType, Double>();
+
+		int yearsOfChange = ModelConfig.IS_CALIBRATION_RUN ? 0 : (timestep.getTimestep() - ModelConfig.CEREAL_CHANGE_START_STEP) * ModelConfig.TIMESTEP_SIZE;
+
+		for (Map.Entry<CropType, Double> entry : demandManager.getBaseCerealFraction(country).entrySet()) {
+			minCerealFraction.put(entry.getKey(), entry.getValue() * Math.max(ModelConfig.TOTAL_MAX_CEREAL_CHANGE, (1- ModelConfig.ANNUAL_MAX_CEREAL_CHANGE * yearsOfChange)));
+		}
+		return minCerealFraction;
+	}
+
+	protected void updateNetImportsFromProdAndDemand(Map<CommodityType, Double> demands, Map<CropType, Double> minCerealFracts, Map<CropType, CropUsageData> cropUsages) {
+
+		for (CommodityType commodity : CommodityType.getAllItems()) {
+			if (commodity != CommodityType.CEREALS && commodity.getCropTypes().size() != 1)
+				throw new RuntimeException("Not cereal and not 1 to 1 mapping for commodity to crop: " + commodity);  // skips cereals which is a special case and handled separately below
+
+			double demand = demands.get(commodity);
+			if (commodity == CommodityType.CEREALS) {
+				Map<CropType, Double> netImportsFromMinDemands = new HashMap<CropType, Double>();
+				double totalProd = 0, totalmportFromMD = 0, totalExcessProd = 0;
+
+				for (CropType crop : commodity.getCropTypes()) {
+					CropUsageData cropUsage = cropUsages.get(crop);
+					double prod = cropUsage.getProduction() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed();
+					totalProd += prod;
+					double minFract = minCerealFracts.containsKey(crop) ? minCerealFracts.get(crop) : 0.0;
+					double netImportsFromMinDemand = minFract * demand - prod;
+					LogWriter.println("netImportsFromMinDemand " + crop + " to " + netImportsFromMinDemand);
+					netImportsFromMinDemands.put(crop, netImportsFromMinDemand);
+					
+					if (netImportsFromMinDemand > 0)
+						totalmportFromMD += netImportsFromMinDemand;
+					else
+						totalExcessProd += netImportsFromMinDemand;
+				}
+				double additionalNetImportsRequired = demand - totalProd - totalmportFromMD;
+				LogWriter.println("additionalNetImportsRequired is " + additionalNetImportsRequired);
+
+				for (CropType crop : commodity.getCropTypes()) {
+					double netImportsMD = netImportsFromMinDemands.get(crop);
+					double netImports=0;
+					if (additionalNetImportsRequired > 0) { // overall need to import more
+						if (minCerealFracts.containsKey(crop))
+							netImports = netImportsMD + minCerealFracts.get(crop) * additionalNetImportsRequired; // divide required additional imports by minCerealFracts		
+					}
+					else {  // overall need to export more
+						if (netImportsMD > 0)
+							netImports = netImportsMD; // still import what we need for minimum fraction
+						else if(netImportsMD < 0)
+							netImports = netImportsMD / totalExcessProd * additionalNetImportsRequired; // divide exports by production in excess of minimum demand
+					}
+					
+					CropUsageData cropUsage = cropUsages.get(crop);	
+					LogWriter.println("Updating cereal net imports " + crop + " to " + netImports);
+					cropUsage.updateNetImports(netImports);
+				}
+			}
+			else {
+				// simple 1-1 commodity to cereal mappings
+				for (CropType crop : commodity.getCropTypes()) {
+					CropUsageData cropUsage = cropUsages.get(crop);
+					double prod =  cropUsage.getProduction() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed();
+					double netImports = demand - prod;
+					LogWriter.println("Updating net imports " + crop + " to " + netImports);
+					cropUsage.updateNetImports(netImports);
+				}
+			} 
+		} 
+	}
+	
+	abstract public Map<CropType, CropUsageData> getCropUsageData();
+}
diff --git a/src/ac/ed/lurg/country/CompositeCountry.java b/src/ac/ed/lurg/country/CompositeCountry.java
index 16286ba51ad3f4e9d58ebf23ff697512ded54c7a..ac0f42a31e05dbd38de77fce41070a8bd27fdd5e 100644
--- a/src/ac/ed/lurg/country/CompositeCountry.java
+++ b/src/ac/ed/lurg/country/CompositeCountry.java
@@ -24,5 +24,29 @@ public class CompositeCountry {
 	public String toString() {
 		return 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;
+		CompositeCountry other = (CompositeCountry) 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/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
index f2c40d678e2a79b24d6c30c7c2065c9cafd22839..7d044667f3f513668b785aa3b7c65502081e642b 100644
--- a/src/ac/ed/lurg/country/CountryAgent.java
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -1,5 +1,10 @@
 package ac.ed.lurg.country;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -30,36 +35,27 @@ import ac.sac.raster.IntegerRasterItem;
 import ac.sac.raster.RasterKey;
 import ac.sac.raster.RasterSet;
 
-public class CountryAgent {
-
-	private AbstractDemandManager demandManager;
-	private CompositeCountry country;
+public class CountryAgent extends AbstractCountryAgent {
 
 	private GamsRasterOutput previousGamsRasterOutput;
 	private Timestep currentTimestep;
-	private Map<CommodityType, Double> currentProjectedDemand;
-	private Map<CropType, CountryPrice> currentCountryPrices;
-	private Map<CropType, Double> tradeBarriers;
 	private RasterSet<IntegerRasterItem> yieldClusters;
 	private double animalFeedFromOtherSources;
 	private Map<CropType, Double> subsidyRates;
+	private boolean saveGamsGdxFiles;
 
 	public CountryAgent(AbstractDemandManager demandManager,CompositeCountry country, RasterSet<LandUseItem> cropAreaRaster,
 			Map<CropType, CropUsageData> cropUsageData, Map<CropType, Double> tradeBarriers, RasterSet<IntegerRasterItem> yieldClusters,
 			Map<CropType, Double> subsidyRates) {
 
-		this.demandManager = demandManager;
-		this.country = country;
-		this.tradeBarriers = tradeBarriers;
+		super(demandManager, country, tradeBarriers);
 		this.yieldClusters = yieldClusters;
 		this.subsidyRates = subsidyRates;
 
 		GamsRasterOutput initialData = new GamsRasterOutput(cropAreaRaster, cropUsageData);
 		previousGamsRasterOutput = initialData;
-	}
-
-	public CompositeCountry getCountry() {
-		return country;
+		
+		saveGamsGdxFiles = (ModelConfig.GAMS_COUNTRY_TO_SAVE != null && country.getName().equals(ModelConfig.GAMS_COUNTRY_TO_SAVE));
 	}
 
 	public RasterSet<IntegerRasterItem> getYieldClusters() {
@@ -110,15 +106,15 @@ public class CountryAgent {
 		currentTimestep = timestep;
 
 		// get projected demand
-		currentProjectedDemand = demandManager.getDemand(country, timestep.getYear());
+		Map<CommodityType, Double> projectedDemand = calculateDemand(timestep);
 		if (currentTimestep.isInitialTimestep()) {
-			double totalAnimalProductDemand = currentProjectedDemand.get(CommodityType.MONOGASTRICS) + currentProjectedDemand.get(CommodityType.RUMINANTS);
+			double totalAnimalProductDemand = projectedDemand.get(CommodityType.MONOGASTRICS) + projectedDemand.get(CommodityType.RUMINANTS);
 			animalFeedFromOtherSources = totalAnimalProductDemand * ModelConfig.ANIMAL_FEED_FROM_OTHER_SOURCES_RATE;
 		}
 			
-		currentCountryPrices = calculateCountryPrices(worldPrices);
+		calculateCountryPrices(worldPrices, currentTimestep);
 			
-		if (currentProjectedDemand.size() == 0) {
+		if (projectedDemand.size() == 0) {
 			LogWriter.printlnError("No demand for country " + country + " so skipping it");
 		}
 		else if (countryYieldSurfaces.size() == 0 ) {
@@ -142,79 +138,25 @@ public class CountryAgent {
 				updateNetImportsFromProdAndDemand(countryInput.getProjectedDemand(), countryInput.getMinCerealFraction(), result.getCropUsageData());
 			}
 			
+			if (saveGamsGdxFiles) saveGDXFile();
+			
 			previousGamsRasterOutput = result;
 			return result;
 		}
 
 		throw new RuntimeException("Skipping optimisation of country " + country);
 	}
-
-	private void updateNetImportsFromProdAndDemand(Map<CommodityType, Double> demands, Map<CropType, Double> minCerealFracts, Map<CropType, CropUsageData> cropUsages) {
-
-		for (CommodityType commodity : CommodityType.getAllItems()) {
-			if (commodity != CommodityType.CEREALS && commodity.getCropTypes().size() != 1)
-				throw new RuntimeException("Not cereal and not 1 to 1 mapping for commodity to crop: " + commodity);  // skips cereals which is a special case and handled separately below
-
-			double demand = demands.get(commodity);
-			if (commodity == CommodityType.CEREALS) {
-				Map<CropType, Double> netImportsFromMinDemands = new HashMap<CropType, Double>();
-				double totalProd = 0, totalmportFromMD = 0, totalExcessProd = 0;
-
-				for (CropType crop : commodity.getCropTypes()) {
-					CropUsageData cropUsage = cropUsages.get(crop);
-					double prod = cropUsage.getProduction() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed();
-					totalProd += prod;
-					double minFract = minCerealFracts.containsKey(crop) ? minCerealFracts.get(crop) : 0.0;
-					double netImportsFromMinDemand = minFract * demand - prod;
-					LogWriter.println("netImportsFromMinDemand " + crop + " to " + netImportsFromMinDemand);
-					netImportsFromMinDemands.put(crop, netImportsFromMinDemand);
-					
-					if (netImportsFromMinDemand > 0)
-						totalmportFromMD += netImportsFromMinDemand;
-					else
-						totalExcessProd += netImportsFromMinDemand;
-				}
-				double additionalNetImportsRequired = demand - totalProd - totalmportFromMD;
-				LogWriter.println("additionalNetImportsRequired is " + additionalNetImportsRequired);
-
-				for (CropType crop : commodity.getCropTypes()) {
-					double netImportsMD = netImportsFromMinDemands.get(crop);
-					double netImports=0;
-					if (additionalNetImportsRequired > 0) { // overall need to import more
-						if (minCerealFracts.containsKey(crop))
-							netImports = netImportsMD + minCerealFracts.get(crop) * additionalNetImportsRequired; // divide required additional imports by minCerealFracts		
-					}
-					else {  // overall need to export more
-						if (netImportsMD > 0)
-							netImports = netImportsMD; // still import what we need for minimum fraction
-						else if(netImportsMD < 0)
-							netImports = netImportsMD / totalExcessProd * additionalNetImportsRequired; // divide exports by production in excess of minimum demand
-					}
-					
-					CropUsageData cropUsage = cropUsages.get(crop);	
-					LogWriter.println("Updating cereal net imports " + crop + " to " + netImports);
-					cropUsage.updateNetImports(netImports);
-				}
-			}
-			else {
-				// simple 1-1 commodity to cereal mappings
-				for (CropType crop : commodity.getCropTypes()) {
-					CropUsageData cropUsage = cropUsages.get(crop);
-					double prod =  cropUsage.getProduction() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed();
-					double netImports = demand - prod;
-					LogWriter.println("Updating net imports " + crop + " to " + netImports);
-					cropUsage.updateNetImports(netImports);
-				}
-			} 
-		} 
-	}
-
-	public Map<CommodityType, Double> getCurrentProjectedDemand() {
-		return currentProjectedDemand;
-	}
 	
-	public Map<CropType, CountryPrice> getCurrentCountryPrices() {
-		return currentCountryPrices;
+	private void saveGDXFile() {
+		// some hacky code for debug purposes that keeps each gams gdx file
+		try {
+			Files.copy(
+					FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + "_gams_java_gdb1.gdx"),
+					FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + ModelConfig.GAMS_COUNTRY_TO_SAVE + currentTimestep.getYear() + ".gdx"),
+					StandardCopyOption.REPLACE_EXISTING);
+		} catch (IOException e) {
+			LogWriter.print(e);
+		}
 	}
 
 	private GamsRasterInput getGamsRasterInput(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces, double gen2EcIncrease) {
@@ -269,36 +211,13 @@ public class CountryAgent {
 
 		Map<CropType, Double> minCerealFract = getMinCerealFraction(currentTimestep);
 
-		GamsCountryInput countryLevelInputs = new GamsCountryInput(country, currentProjectedDemand, currentCountryPrices, importConstraints, 
+		GamsCountryInput countryLevelInputs = new GamsCountryInput(country, getCurrentProjectedDemand(), getCurrentCountryPrices(), importConstraints, 
 				previousGamsRasterOutput.getCropUsageData(), minCerealFract, animalFeedFromOtherSources, subsidyRates);	
 		GamsRasterInput input = new GamsRasterInput(currentTimestep, countryYieldSurfaces, previousGamsRasterOutput.getLandUses(), irrigData, countryLevelInputs);
 
 		return input;
 	}
 
-	private Map<CropType, Double> getMinCerealFraction(Timestep timestep) {
-		Map<CropType, Double> minCerealFraction = new HashMap<CropType, Double>();
-
-		int yearsOfChange = ModelConfig.IS_CALIBRATION_RUN ? 0 : (timestep.getTimestep() - ModelConfig.TECHNOLOGY_CHANGE_START_STEP) * ModelConfig.TIMESTEP_SIZE;
-
-		for (Map.Entry<CropType, Double> entry : demandManager.getBaseCerealFraction(country).entrySet()) {
-			minCerealFraction.put(entry.getKey(), entry.getValue() * Math.max(ModelConfig.TOTAL_MAX_CEREAL_CHANGE, (1- ModelConfig.ANNUAL_MAX_CEREAL_CHANGE * yearsOfChange)));
-		}
-		return minCerealFraction;
-	}
-
-	private Map<CropType, CountryPrice> calculateCountryPrices(Map<CropType, GlobalPrice> worldPrices){
-		Map<CropType, CountryPrice> countryPrices = new HashMap <CropType, CountryPrice>();
-
-		for (CropType c : CropType.getImportedTypes()) {
-			GlobalPrice worldPrice = worldPrices.get(c);
-			CountryPrice prices = new CountryPrice(worldPrice.getCountryImportPrice(tradeBarriers.get(c), currentTimestep), worldPrice.getExportPrice());
-			countryPrices.put(c, prices);
-		}
-
-		return countryPrices;
-	}
-
 	public RasterSet<LandUseItem> getLandUses() {
 		return previousGamsRasterOutput.getLandUses();
 	}
@@ -306,4 +225,4 @@ public class CountryAgent {
 	public Map<CropType, CropUsageData> getCropUsageData() {
 		return previousGamsRasterOutput.getCropUsageData();
 	}
-}
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/CountryAgentManager.java b/src/ac/ed/lurg/country/CountryAgentManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c59266712c3800427b72d0b7068906098dee646
--- /dev/null
+++ b/src/ac/ed/lurg/country/CountryAgentManager.java
@@ -0,0 +1,128 @@
+package ac.ed.lurg.country;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import ac.ed.lurg.InternationalMarket;
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.Timestep;
+import ac.ed.lurg.country.crafty.CraftyCountryAgent;
+import ac.ed.lurg.country.crafty.CraftyProdManager;
+import ac.ed.lurg.demand.AbstractDemandManager;
+import ac.ed.lurg.landuse.CropUsageData;
+import ac.ed.lurg.landuse.IrrigationItem;
+import ac.ed.lurg.landuse.IrrigationRasterSet;
+import ac.ed.lurg.landuse.LandUseItem;
+import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.utils.LogWriter;
+import ac.ed.lurg.yield.YieldRaster;
+import ac.sac.raster.IntegerRasterItem;
+import ac.sac.raster.RasterKey;
+import ac.sac.raster.RasterSet;
+
+public class CountryAgentManager {
+	private AbstractDemandManager demandManager;
+	private TradeManager tradeBarrierManager;
+	private SubsidyRateManager subsidyRateManager;
+	private InternationalMarket internationalMarket;
+	private CountryBoundaryRaster countryBoundaryRaster;
+	private RasterSet<IntegerRasterItem> clusterIdRaster;
+	private RasterSet<LandUseItem> globalLandUseRaster;
+	
+	private Collection<AbstractCountryAgent> countryAgents = new ArrayList<AbstractCountryAgent>();
+	private Collection<CountryAgent> gamsCountryAgents = new ArrayList<CountryAgent>();
+	private Collection<CraftyCountryAgent> craftyCountryAgents = new ArrayList<CraftyCountryAgent>();
+	private CraftyProdManager craftyManager;
+	
+	public CountryAgentManager (CompositeCountryManager compositeCountryManager, AbstractDemandManager demandManager, 
+			CountryBoundaryRaster countryBoundaryRaster, InternationalMarket internationalMarket, 
+			RasterSet<IntegerRasterItem> clusterIdRaster, RasterSet<LandUseItem> globalLandUseRaster) {
+		this.demandManager = demandManager;
+		this.countryBoundaryRaster = countryBoundaryRaster;
+		this.clusterIdRaster = clusterIdRaster;
+		this.internationalMarket = internationalMarket;
+		this.globalLandUseRaster = globalLandUseRaster;
+		tradeBarrierManager = new TradeManager(compositeCountryManager);
+		subsidyRateManager = new SubsidyRateManager(compositeCountryManager);
+		craftyManager = new CraftyProdManager();
+	}
+	
+	public void addForCountry(CompositeCountry cc, Map<CompositeCountry, Map<CropType, CropUsageData>> cropUsageDataMap, RasterSet<LandUseItem> initLU) {
+		if (ModelConfig.DEBUG_LIMIT_COUNTRIES) { // DEBUG code
+			if (!(cc.getName().equals(ModelConfig.DEBUG_COUNTRY_NAME)))
+				return;
+		}
+
+		Map<CropType, Double> tradeBarriers = tradeBarrierManager.getTradeBarriers(cc);
+
+		if (ModelConfig.USE_CRAFTY_COUNTRIES && craftyManager.getCraftyCountries().contains(cc)) {
+			LogWriter.println("Creating CRAFTY agent for: " + cc);
+			CraftyCountryAgent cca = new CraftyCountryAgent(demandManager, cc, tradeBarriers);
+			craftyCountryAgents.add(cca);
+			countryAgents.add(cca);
+		}
+		else { //GAMS
+			LogWriter.println("Creating GAMS agent for: " + cc);
+			List<RasterKey> keys = countryBoundaryRaster.getKeysFor(cc);
+			Map<CropType, CropUsageData> countryCommodityData = cropUsageDataMap.get(cc);
+	
+			if (countryCommodityData == null) {
+				LogWriter.printlnError("No commodities data for " + cc + ", so skipping");
+			} else {
+				RasterSet<LandUseItem> initCountryLandUse = initLU.createSubsetForKeys(keys);
+				RasterSet<IntegerRasterItem> yieldClusters = ModelConfig.GENERATE_NEW_YIELD_CLUSTERS ? null : clusterIdRaster.createSubsetForKeys(keys);
+	
+				if (initCountryLandUse.size() == 0) {
+					LogWriter.printlnError("No initial land use for " + cc + ", so skipping");
+				}
+				else {
+					Map<CropType, Double> subsidyRates = subsidyRateManager.getSubsidyRates(cc);
+		
+					CountryAgent ca = new CountryAgent(demandManager, cc, initCountryLandUse, countryCommodityData, tradeBarriers, yieldClusters, subsidyRates);
+					gamsCountryAgents.add(ca);
+					countryAgents.add(ca);
+				}
+			}
+		}
+	}
+	
+	public Collection<AbstractCountryAgent> getAll() {
+		return countryAgents;
+	}
+
+	public void processTimestepForAll(Timestep timestep, YieldRaster yieldSurfaces, IrrigationRasterSet currentIrrigationData, double gen2Increase) {
+		
+		for (CountryAgent ca : gamsCountryAgents) {		
+			LogWriter.println("Country " + ca.getCountry());
+			doGamsAgentTimestep(timestep, ca, yieldSurfaces, currentIrrigationData, gen2Increase);
+		}
+		
+		if (craftyCountryAgents.size() > 0) {
+			craftyManager.updateWithCraftyData(craftyCountryAgents, timestep, internationalMarket.getWorldPrices());  // this will wait for the marker file from CRAFTY
+		}
+	}
+	
+	private void doGamsAgentTimestep(Timestep timestep, CountryAgent ca, YieldRaster yieldSurfaces, IrrigationRasterSet currentIrrigationData, double gen2Increase) {
+		Collection<RasterKey> countryKeys = countryBoundaryRaster.getKeysFor(ca.getCountry());
+		YieldRaster countryYieldSurfaces = yieldSurfaces.createSubsetForKeys(countryKeys);
+		RasterSet<IrrigationItem> irrigData = currentIrrigationData.createSubsetForKeys(countryKeys);
+		
+		// do the optimization
+		try {
+			ca.determineProduction(timestep, countryYieldSurfaces, irrigData, internationalMarket.getWorldPrices(), gen2Increase);
+			
+			// update global rasters
+			globalLandUseRaster.putAll(ca.getLandUses());
+
+			// if first timestep and calibration get the clustering info, which doesn't change through time
+			if (ModelConfig.GENERATE_NEW_YIELD_CLUSTERS && timestep.isInitialTimestep())
+				clusterIdRaster.putAll(ca.getYieldClusters());
+			
+		} catch (Exception e) {
+			LogWriter.printlnError("Exception processing " + ca.getCountry() + " will continue with other countries");
+			LogWriter.print(e);
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
new file mode 100644
index 0000000000000000000000000000000000000000..24a99c2b750abc2debc6bb110c3d3d3032cc53c9
--- /dev/null
+++ b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java
@@ -0,0 +1,43 @@
+package ac.ed.lurg.country.crafty;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import ac.ed.lurg.Timestep;
+import ac.ed.lurg.country.AbstractCountryAgent;
+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.types.CropType;
+
+/** 
+ * Country agent that is interface over data transferred to and from CRAFTY.
+ * Need to configure country mapping to that there is 
+ * */
+public class CraftyCountryAgent extends AbstractCountryAgent {
+	
+	private Map<CropType, CropUsageData> cropUsageData;
+
+	public CraftyCountryAgent(AbstractDemandManager demandManager,CompositeCountry country, Map<CropType, Double> tradeBarriers) {
+		super(demandManager, country, tradeBarriers);
+	}
+
+	public Map<CropType, CropUsageData> getCropUsageData() {
+		return cropUsageData;
+	}
+
+	public void updateProduction(Timestep timestep, Map<CropType, Double> cropProduction, Map<CropType, GlobalPrice> worldPrices) {
+		calculateDemand(timestep);
+		calculateCountryPrices(worldPrices, timestep);
+
+		cropUsageData = new HashMap<CropType, CropUsageData>();
+		for (Entry<CropType, Double> entry : cropProduction.entrySet()) {
+			CropUsageData cropusage = new CropUsageData(entry.getValue());
+			cropUsageData.put(entry.getKey(), cropusage);
+		}
+		
+		updateNetImportsFromProdAndDemand(currentProjectedDemand, getMinCerealFraction(timestep), cropUsageData);
+	}
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/crafty/CraftyProdManager.java b/src/ac/ed/lurg/country/crafty/CraftyProdManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..af3cf49422d19e3da306e3b5f2f37a0cff707fe8
--- /dev/null
+++ b/src/ac/ed/lurg/country/crafty/CraftyProdManager.java
@@ -0,0 +1,76 @@
+package ac.ed.lurg.country.crafty;
+
+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.ModelConfig;
+import ac.ed.lurg.Timestep;
+import ac.ed.lurg.country.CompositeCountry;
+import ac.ed.lurg.country.GlobalPrice;
+import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.utils.LogWriter;
+import ac.ed.lurg.utils.StringTabularReader;
+import ac.ed.lurg.utils.WatchForFile;
+
+public class CraftyProdManager {
+
+	private Collection<CompositeCountry> craftyCountries;
+	
+	public synchronized Collection<CompositeCountry> getCraftyCountries() {
+		if (craftyCountries == null) {
+			craftyCountries = new ArrayList<CompositeCountry>();
+			StringTabularReader 	tabularReader = new StringTabularReader(",", new String[]{"Country"});
+			List<Map<String, String>> rows = tabularReader.read(ModelConfig.CRAFTY_PRODUCTION_DIR + File.separator + "craftyCountries.csv");
+			for (Map<String, String> row : rows) {
+				craftyCountries.add(new CompositeCountry(row.get("Country"), "craftycountry"));
+			}
+		}
+		return craftyCountries;
+	}
+
+	public void updateWithCraftyData(Collection<CraftyCountryAgent> craftyCountryAgents, Timestep timestep, Map<CropType, GlobalPrice> worldPrices) {
+		String rootDir = ModelConfig.CRAFTY_PRODUCTION_DIR + File.separator + timestep.getYear();
+		long startTime = System.currentTimeMillis();
+		
+		WatchForFile fileWatcher = new WatchForFile(new File(rootDir + File.separator + "done"));
+		boolean foundFile = fileWatcher.await(ModelConfig.LPJG_MONITOR_TIMEOUT_SEC);
+		if (!foundFile) {
+			LogWriter.printlnError("Not able to find marker file.  May have timed out.");
+			throw new RuntimeException("Not able to find marker file.  May have timed out.");
+		}
+		LogWriter.println("Found marker file in " + (System.currentTimeMillis() - startTime) + " ms");
+		
+		StringTabularReader 	tabularReader = new StringTabularReader(",", new String[]{"Country","Crop","Production"});
+		tabularReader.read(rootDir + File.separator + "production.csv");
+
+		
+		for (CraftyCountryAgent cca : craftyCountryAgents) {
+			Map<CropType, Double> cropProduction = new HashMap<CropType, Double>();
+	
+			Map<String, String> queryMap = new HashMap<String, String>();
+			queryMap.put("Country", cca.getCountry().getName());
+
+			try {
+				List<Map<String, String>> rows = tabularReader.query(queryMap);
+				for (Map<String, String> row : rows) {
+					String cropS = row.get("Crop");
+					CropType crop = CropType.getForGamsName(cropS);
+					String prodS = row.get("Production");
+					Double prod = Double.valueOf(prodS);
+					cropProduction.put(crop, prod);
+				}
+				if (cropProduction.size() < CropType.getImportedTypes().size()) {  // Don't need setaside or pasture, which aren't imported either
+					LogWriter.printlnError("Not all crops present in Crafty production for country: " + cca.getCountry() + " only " + cropProduction.size());
+				}
+				cca.updateProduction(timestep, cropProduction, worldPrices);
+			}
+			catch (Exception e) {
+				LogWriter.println("Problem getting Crafty data for: " + cca.getCountry());
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/ac/ed/lurg/landuse/CropUsageData.java b/src/ac/ed/lurg/landuse/CropUsageData.java
index a7640fccbe16f27ca45010c59ff146f7e86cc7f8..aaa9019951914c05c7fe3f5848a38db7648eb9c0 100644
--- a/src/ac/ed/lurg/landuse/CropUsageData.java
+++ b/src/ac/ed/lurg/landuse/CropUsageData.java
@@ -10,8 +10,11 @@ public class CropUsageData {
 	private double prodCost;
 	private double area;
 
+	public CropUsageData(double prod) {
+		this.prod = prod;
+	}
+	
 	public CropUsageData(double ruminantFeed, double monogastricFeed, double netImports, double netImportCost, double prod, double prodCost, double area) {
-		super();
 		this.ruminantFeed = ruminantFeed;
 		this.monogastricFeed = monogastricFeed;
 		this.netImports = netImports;