From bb4a660e882e90fae5c9a6ffea189a40e03363b2 Mon Sep 17 00:00:00 2001
From: Peter Alexander <peter@blackhillock.co.uk>
Date: Wed, 8 Jul 2015 20:56:39 +0100
Subject: [PATCH] Fixes, including handling pasture and meat separately

---
 GAMS/IntExtOpt.gms                            | 46 ++++++++++---------
 src/ac/ed/lurg/ModelConfig.java               |  2 +-
 .../country/gams/GamsLocationOptimiser.java   |  9 ++--
 .../country/gams/GamsRasterOptimiser.java     |  6 +--
 src/ac/ed/lurg/types/CropType.java            | 40 ++++++++++++++--
 .../lurg/yield/LPJYieldResponseMapReader.java | 41 +++++++++++------
 src/ac/ed/lurg/yield/YieldResponse.java       |  7 ++-
 src/ac/ed/lurg/yield/YieldResponsesItem.java  |  4 ++
 8 files changed, 106 insertions(+), 49 deletions(-)

diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms
index 4ed20694..603cdfc0 100644
--- a/GAMS/IntExtOpt.gms
+++ b/GAMS/IntExtOpt.gms
@@ -6,6 +6,8 @@
  SET non_cereal_crop(crop_less_pasture)                                         / oilcrops, soybean, pulses, starchyRoots /;
  SET feed_crop(crop)                       / wheat, maize,                        oilcrops, soybean /;
  SET not_feed_crop(crop)                                 / rice, tropicalCereals,                    pulses, starchyRoots, pasture /;
+ SET import_crop(all_types) / meat,          wheat, maize, rice, tropicalCereals, oilcrops, soybean, pulses, starchyRoots /;
+ 
  
  SET location;
  PARAMETER suitableLandArea(location)        areas of land in Mha;
@@ -17,9 +19,9 @@
  PARAMETER fertParam(crop, location)         yield response to fertilizer parameter;
  PARAMETER irrigParam(crop, location)        yield response to irrigation parameter;
  PARAMETER demand(all_types)                 in t;
- PARAMETER worldInputEnergy(crop)            average input energy from world exports used to determine if we should import or export;
- PARAMETER maxNetImport(crop)                maximum net import for each crop based on world market;
- PARAMETER minNetImport(crop)                minimum net import for each crop based on world market;
+ PARAMETER worldInputEnergy(all_types)       average input energy from world exports used to determine if we should import or export;
+ PARAMETER maxNetImport(import_crop)         maximum net import for each crop based on world market;
+ PARAMETER minNetImport(import_crop)         minimum net import for each crop based on world market;
  PARAMETER irrigCost(location)               irrigation cost;
  
  SCALAR meatEfficency                efficiency of converting feed and pasture into animal products;
@@ -35,17 +37,15 @@ $load meatEfficency, maxLandUseChange, minFeedRate, irrigCost
 $gdxin
   
  SCALAR delta use to smooth power function see 7.5 www.gams.com dd docs solversconopt.pdf / 0.00000000001 /  
- 
-* display demand;
-    
+     
  SCALAR minDemandPerCereal / 0.1 /
-               
- PARAMETER feedDM(crop) energy from feed in MJ per t (wet) (need to check these figures)
-          /   wheat            0.87
-              maize            0.87
-              oilcrops         0.32
-              soybean          0.87
-              pasture          0.2 / ;
+         
+ PARAMETER feedDM(crop) energy from feed in MJ per kg (wet) (i.e. MJ per kg DM multiplied by DM kg per kg)
+          /   wheat     11.7   
+              maize     11.1  
+              oilcrops  10.8  
+              soybean   10.6  
+              pasture   5.3    / ; 
  
  VARIABLES
        area(crop, location)               total area for each crop - Mha
@@ -53,7 +53,7 @@ $gdxin
        irrigI(crop, location)             irrigation intensity for each crop - factor between 0 and 1
        otherIntensity(crop, location)     other intensity for each crop - unit less
        feedAmount(crop)                   amount of feed for each crop - Mt
-       netImportAmount(all_types)         net imports of crops and meat - Mt
+       netImportAmount(all_types)       net imports of crops and meat - Mt
        yield(crop, location)              yield per area for each crop - t per ha
        unitEnergy(crop, location)         energy per area for each crop - energy
        net_supply(crop_less_pasture)      supply after exports and feed
@@ -75,21 +75,22 @@ $gdxin
        MEAT_DEMAND_CONSTRAINT                           satisfy demand for meat
        MAX_FERT_INTENSITY_CONSTRAINT(crop, location)    constraint on maximum fertilizer intensity
        MAX_IRRIG_INTENSITY_CONSTRAINT(crop, location)   constraint on maximum irrigation intensity
+*       MAX_OTHER_INTENSITY_CONSTRAINT(crop, location)   constraint on maximum other intensity
        TOTAL_LAND_CHANGE_CONSTRAINT(location)           constraint on the rate of land use change 
        CROPLAND_CHANGE_CONSTRAINT(location)             constraint on the rate of land use change 
        PASTURE_CHANGE_CONSTRAINT(location)              constraint on the rate of land use change 
        AGRI_LAND_INCREASE_CONSTRAINT(location)          constraint expansion of agricultural land
        AGRI_LAND_DECREASE_CONSTRAINT(location)          constraint expansion of agricultural land
        NON_FEED_CROP_CONSTRAINT(not_feed_crop)          constraint to set non feed crop feed usage to zero
-       MAX_NET_IMPORT_CONSTRAINT(crop)                  constraint on max net imports
-       MIN_NET_IMPORT_CONSTRAINT(crop)                  constraint on min net imports
+       MAX_NET_IMPORT_CONSTRAINT(import_crop)           constraint on max net imports
+       MIN_NET_IMPORT_CONSTRAINT(import_crop)           constraint on min net imports
        MIN_FEED_CONSTRAINT                              constraint on min feed rate
        NET_SUPPLY_EQ(crop_less_pasture)                 calc net supply for crops
        ENERGY_EQ                                        total energy objective function;
  
  UNIT_ENERGY_EQ(crop, location) .. unitEnergy(crop, location) =E= fertI(crop, location) + 
-                                                                       (1+irrigI(crop, location)*irrigCost(location) ** 1.3) +
-                                                                       (1+otherIntensity(crop, location) ** 2);
+                                                                     ((1+irrigI(crop, location) ** 2) * irrigCost(location)) +
+                                                                     (1+otherIntensity(crop, location) ** 2);
  
  YIELD_EQ(crop, location) .. yield(crop, location) =E= (
                yieldNone(crop, location) + 
@@ -119,6 +120,7 @@ $gdxin
     
  MAX_FERT_INTENSITY_CONSTRAINT(crop, location) .. fertI(crop, location) =L= 1;
  MAX_IRRIG_INTENSITY_CONSTRAINT(crop, location) .. irrigI(crop, location) =L= 1;
+* MAX_OTHER_INTENSITY_CONSTRAINT(crop, location) .. otherIntensity(crop, location) =L= 10;
  
  TOTAL_LAND_CHANGE_CONSTRAINT(location) .. suitableLandArea(location) =G= sum(crop, area(crop, location));
  
@@ -132,20 +134,20 @@ $gdxin
  
  NON_FEED_CROP_CONSTRAINT(not_feed_crop) .. feedAmount(not_feed_crop) =E= 0;
  
- MAX_NET_IMPORT_CONSTRAINT(crop) .. netImportAmount(crop) =L= maxNetImport(crop);
+ MAX_NET_IMPORT_CONSTRAINT(import_crop) .. netImportAmount(import_crop) =L= maxNetImport(import_crop);
  
- MIN_NET_IMPORT_CONSTRAINT(crop) .. netImportAmount(crop) =G= minNetImport(crop);
+ MIN_NET_IMPORT_CONSTRAINT(import_crop) .. netImportAmount(import_crop) =G= minNetImport(import_crop);
  
  MIN_FEED_CONSTRAINT .. sum(feed_crop, feedDM(feed_crop) * feedAmount(feed_crop)) =G= minFeedRate * (demand('meat') - netImportAmount('meat'));
  
  ENERGY_EQ .. energy =E= (SUM((crop, location), area(crop, location) * unitEnergy(crop, location))
             + sum(location, sum(crop, area(crop, location)) - sum(crop, previousArea(crop, location)) * landChangeEnergy)
-            + sum(crop, (netImportAmount(crop)) * worldInputEnergy(crop))) / 1000000;
+            + sum(import_crop, (netImportAmount(import_crop)) * worldInputEnergy(import_crop))) / 1000000;
  
  MODEL LAND_USE /ALL/ ;
  SOLVE LAND_USE USING NLP MINIMIZING energy;
    
-display net_supply.l, area.l, yield.l, feedAmount.l, netImportAmount.l
+display feedAmount.l, minFeedRate, demand, netImportAmount.l;
     
  Scalar ms 'model status', ss 'solve status'; 
  ms=LAND_USE.modelstat;
diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
index 1d999e92..63a07041 100644
--- a/src/ac/ed/lurg/ModelConfig.java
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -93,7 +93,7 @@ public class ModelConfig {
 	public static final String BASELINE_CONSUMP_FILE = DATA_DIR + File.separator + "base_consump.csv";
 	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 String COMMODITY_DATA_FILE = DATA_DIR + File.separator + "con_prod_c.csv";
+	public static final String COMMODITY_DATA_FILE = DATA_DIR + File.separator + "con_prod_c_and_m.csv";
 
 	// yield data
 	public static final String YIELD_DIR = "/Users/peteralexander/Documents/LURG/LPJ/tom-April2015";
diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
index 0fc28992..6b82ca3b 100644
--- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
@@ -60,6 +60,9 @@ public class GamsLocationOptimiser {
 
 		GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.GAMS_MODEL);
 		GAMSOptions opt = ws.addOptions();
+		
+		opt.setAllModelTypes("conopt");
+		
 		opt.defines("gdxincname", inDB.getName());
 
 		long startTime = System.currentTimeMillis();
@@ -128,7 +131,7 @@ public class GamsLocationOptimiser {
 			String locString = Integer.toString(locationId);
 			YieldResponsesItem yresp = entry.getValue();
 
-			for (CropType crop : CropType.values()) {
+			for (CropType crop : CropType.getNonMeatTypes()) {
 				if (DEBUG) LogWriter.println(String.format("     %15s,\t %.1f,\t %.1f, \t %.1f,\t %.1f,\t\t [%.2f],\t [%.2f]", 
 						crop.getGamsName(), yresp.getYieldNone(crop), yresp.getYieldFertOnly(crop), yresp.getYieldIrrigOnly(crop), yresp.getYieldMax(crop), yresp.getFertParam(crop), yresp.getIrrigParam(crop)));
 
@@ -205,8 +208,8 @@ public class GamsLocationOptimiser {
 
 			if (!cropType.equals(prevCropType)) {
 				feedAmount = varFeedAmount.findRecord(itemName).getLevel();
-				netImport = varNetImports.findRecord(itemName).getLevel();
-
+				netImport = cropType.isImportedCrop() ? varNetImports.findRecord(itemName).getLevel() : 0;
+					
 				cropUsageData.put(cropType, new CropUsageData(feedAmount, netImport));
 				if (DEBUG) LogWriter.println(String.format("\n%s:\tfeedAmount= %.1f,\tnetImports= %.3f", itemName, feedAmount, netImport)); 
 			}
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
index 7d839add..c05d29b6 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
@@ -191,8 +191,8 @@ public class GamsRasterOptimiser {
 
 		}
 
-		int numCerealCats = 1;
-		int numPastureCats = 1;
+		int numCerealCats = 2;
+		int numPastureCats = 2;
 
 		int thisShouldLookAtCropsOtherThanJustWheat; // need to consider other crops, and perhaps other yieldTypes as well
 		List<Double> wheatlDivisions = getDivisions(yieldRaster, CropType.WHEAT, numCerealCats); 
@@ -253,7 +253,7 @@ public class GamsRasterOptimiser {
 				}
 
 				// Crop yields
-				for (CropType crop : CropType.values()) {
+				for (CropType crop : CropType.getNonMeatTypes()) {
 					for (YieldType yieldType : YieldType.values()) {
 						avgYResp.setYield(yieldType, crop, yresp.getYield(yieldType, crop));
 					}
diff --git a/src/ac/ed/lurg/types/CropType.java b/src/ac/ed/lurg/types/CropType.java
index 6f7d5a69..8ed5b07e 100644
--- a/src/ac/ed/lurg/types/CropType.java
+++ b/src/ac/ed/lurg/types/CropType.java
@@ -2,6 +2,7 @@ package ac.ed.lurg.types;
 
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 
 import ac.ed.lurg.utils.LogWriter;
@@ -16,16 +17,45 @@ public enum CropType {
 	SOYBEAN("Soyabeans", "soybean"),
 	PULSES("Pulses + (Total)", "pulses"),
 	STARCHY_ROOTS("Starchy Roots + (Total)", "starchyRoots"),
-	PASTURE("meatmilkeggs", "pasture");
+	MEAT("meatmilkeggs", "meat", false, true),
+	PASTURE("pasture", "pasture", false, false);
 
 	private String faoName;
 	private String gamsName;
-	
-	CropType (String faoName, String gamsName) {
+	private boolean importedCrop;
+	private boolean isMeat;
+
+	CropType (String faoName, String gamsName, boolean importedCrop, boolean isMeat) {
 		this.faoName = faoName;
 		this.gamsName = gamsName;
+		this.importedCrop = importedCrop;
+		this.isMeat = isMeat;
+	}
+	
+	CropType (String faoName, String gamsName) {
+		this(faoName, gamsName, true, false);
+	}
+	
+	public static Collection<CropType> getImportedTypes() {
+		Collection<CropType> comms = new HashSet<CropType>();
+		
+		for (CropType c : values())
+			if (c.importedCrop)
+				comms.add(c);
+		
+		return comms;
 	}
 	
+	public static Collection<CropType> getNonMeatTypes() {
+		Collection<CropType> comms = new HashSet<CropType>();
+		
+		for (CropType c : values())
+			if (!c.isMeat)
+				comms.add(c);
+		
+		return comms;
+	}
+
 	private static final Map<String, CropType> faoNameCache = new HashMap<String, CropType>();
 	private static final Map<String, CropType> gamsNameCache = new HashMap<String, CropType>();
     static {
@@ -67,4 +97,8 @@ public enum CropType {
 	public String getGamsName() {
 		return gamsName;
 	}
+	
+	public boolean isImportedCrop() {
+		return importedCrop;
+	}
 }
diff --git a/src/ac/ed/lurg/yield/LPJYieldResponseMapReader.java b/src/ac/ed/lurg/yield/LPJYieldResponseMapReader.java
index 343b7abe..9197fc68 100644
--- a/src/ac/ed/lurg/yield/LPJYieldResponseMapReader.java
+++ b/src/ac/ed/lurg/yield/LPJYieldResponseMapReader.java
@@ -55,28 +55,29 @@ public class LPJYieldResponseMapReader {
 						record.y = d;
 						break;
 					case 2:
-						record.teSW = d;
+						//YieldRecord values in kg DM / m2, we want t/ha, so 10 times larger
+						record.teSW = d * 10;
 						break;
 					case 3:
-						record.teSWirr = d;
+						record.teSWirr = d * 10;
 						break;
 					case 4:
-						record.teWW = d;
+						record.teWW = d * 10;
 						break;
 					case 5:
-						record.teWWirr = d;
+						record.teWWirr = d * 10;
 						break;
 					case 6:
-						record.teCo = d;
+						record.teCo = d * 10;
 						break;
 					case 7:
-						record.teCoirr = d;
+						record.teCoirr = d * 10;
 						break;
 					case 8:
-						record.trRi = d;
+						record.trRi = d * 10;
 						break;
 					case 9:
-						record.trRiirr = d;
+						record.trRiirr = d * 10;
 						break;
 					}
 
@@ -105,11 +106,25 @@ public class LPJYieldResponseMapReader {
 	
 	public void setData(YieldType noIrrigYieldType, YieldType maxIrrigYieldType, YieldResponsesItem item, YieldRecord record) {
 		
-		//YieldRecord values in kg DM / m2, we want t/ha, so 10 times larger
-		for (CropType crop : CropType.getAllItems()) {
-			item.setYield(noIrrigYieldType, crop, record.teSW * 10);
-			item.setYield(maxIrrigYieldType, crop, record.teSWirr * 10);
-		}
+		item.setYield(noIrrigYieldType, CropType.WHEAT, Math.max(record.teSW, record.teWW));
+		item.setYield(maxIrrigYieldType, CropType.WHEAT, Math.max(record.teSWirr, record.teWWirr));
+		item.setYield(noIrrigYieldType, CropType.MAIZE, record.teCo);
+		item.setYield(maxIrrigYieldType, CropType.MAIZE, record.teCoirr);
+		item.setYield(noIrrigYieldType, CropType.RICE, record.trRi);
+		item.setYield(maxIrrigYieldType, CropType.RICE, record.trRiirr);
+		
+		item.setYield(noIrrigYieldType, CropType.TROPICAL_CEREALS, record.teCo);
+		item.setYield(maxIrrigYieldType, CropType.TROPICAL_CEREALS, record.teCoirr);
+		item.setYield(noIrrigYieldType, CropType.OILCROPS, record.teWW);
+		item.setYield(maxIrrigYieldType, CropType.OILCROPS, record.teWWirr);
+		item.setYield(noIrrigYieldType, CropType.SOYBEAN, record.teSW);
+		item.setYield(maxIrrigYieldType, CropType.SOYBEAN, record.teSWirr);
+		item.setYield(noIrrigYieldType, CropType.PULSES, record.teSW);
+		item.setYield(maxIrrigYieldType, CropType.PULSES, record.teSWirr);
+		item.setYield(noIrrigYieldType, CropType.STARCHY_ROOTS, record.teSW * 1.1);
+		item.setYield(maxIrrigYieldType, CropType.STARCHY_ROOTS, record.teSWirr * 1.1);
+		item.setYield(noIrrigYieldType, CropType.PASTURE, record.teSW * 1.2);
+		item.setYield(maxIrrigYieldType, CropType.PASTURE, record.teSWirr * 1.2);
 	}
 	
 	class YieldRecord {
diff --git a/src/ac/ed/lurg/yield/YieldResponse.java b/src/ac/ed/lurg/yield/YieldResponse.java
index de53cad2..ad89e96c 100644
--- a/src/ac/ed/lurg/yield/YieldResponse.java
+++ b/src/ac/ed/lurg/yield/YieldResponse.java
@@ -4,7 +4,6 @@ import java.util.HashMap;
 import java.util.Map;
 
 import ac.ed.lurg.types.YieldType;
-import ac.ed.lurg.utils.LogWriter;
 
 public class YieldResponse {
 	Map<YieldType, Double> yields = new HashMap<YieldType, Double>();
@@ -21,7 +20,7 @@ public class YieldResponse {
 		Double d = yields.get(type);
 		
 		if (d == null) {
-			LogWriter.printlnError("YieldResponse: No yield found for type " + type);
+		//	LogWriter.printlnError("YieldResponse: No yield found for type " + type);
 			return Double.NaN;
 		}
 		
@@ -30,14 +29,14 @@ public class YieldResponse {
 	
 	public double getFertParam() {
 		if (fertParm == 0)
-			fertParm = calcParam (0, 0.6, 1); // we do have MID fert data, but looks wrong calcParam(getYield(YieldType.NO_FERT_NO_IRRIG), getYield(YieldType.FERT_MID_NO_IRRIG), getYield(YieldType.FERT_MAX_NO_IRRIG));
+			fertParm = calcParam (0, 0.7, 1); // we do have MID fert data, but looks wrong calcParam(getYield(YieldType.NO_FERT_NO_IRRIG), getYield(YieldType.FERT_MID_NO_IRRIG), getYield(YieldType.FERT_MAX_NO_IRRIG));
 		
 		return fertParm;
 	}
 	
 	public double getIrrigParam() {
 		if (irrigParm == 0)
-			irrigParm = calcParam (0, 0.6, 1);  // we don't have a mid irrigation figure, so lets assume 60% at mid point
+			irrigParm = calcParam (0, 0.7, 1);  // we don't have a mid irrigation figure, so lets assume 60% at mid point
 		
 		return irrigParm;
 	}
diff --git a/src/ac/ed/lurg/yield/YieldResponsesItem.java b/src/ac/ed/lurg/yield/YieldResponsesItem.java
index 6a068ca7..cc8f6195 100644
--- a/src/ac/ed/lurg/yield/YieldResponsesItem.java
+++ b/src/ac/ed/lurg/yield/YieldResponsesItem.java
@@ -5,6 +5,7 @@ import java.util.Map;
 
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.types.YieldType;
+import ac.ed.lurg.utils.LogWriter;
 import ac.sac.raster.RasterItem;
 
 public class YieldResponsesItem implements RasterItem {
@@ -26,6 +27,9 @@ public class YieldResponsesItem implements RasterItem {
 	public double getYield(YieldType yieldType, CropType crop) {
 		YieldResponse yr = getYieldResponseForCrop(crop);
 		double d = yr.getYield(yieldType);
+		if (Double.isNaN(d)) {
+			LogWriter.printlnError("YieldResponses: No yield found for type " + yieldType + ", crop " + crop);
+		}
 		return d;
 	}	
 
-- 
GitLab