From a6d10650705d7b0e110ffb5f1a3b09f735f5e371 Mon Sep 17 00:00:00 2001
From: R0slyn <roslyn.henry.08@aberdeen.ac.uk>
Date: Tue, 17 Oct 2017 16:22:52 +0100
Subject: [PATCH] dietary_branch

---
 GAMS/IntExtOpt.gms                            |  7 +-
 src/ac/ed/lurg/ModelConfig.java               | 12 +++-
 src/ac/ed/lurg/ModelMain.java                 | 46 +++++++++++-
 src/ac/ed/lurg/country/CountryAgent.java      | 24 +++++++
 .../country/gams/GamsLocationOptimiser.java   |  6 +-
 .../ed/lurg/demand/AbstractDemandManager.java | 72 ++++++++++++++++---
 src/ac/ed/lurg/types/CommodityType.java       | 26 +++++--
 7 files changed, 170 insertions(+), 23 deletions(-)

diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms
index e1b531c6..9a011219 100644
--- a/GAMS/IntExtOpt.gms
+++ b/GAMS/IntExtOpt.gms
@@ -231,7 +231,7 @@ $gdxin
  parameter totalCropland(location);
  parameter netImportAmount(all_types);
  parameter netImportCost(all_types);
- parameter feedCost(all_types);
+ parameter feedCostRate(feed_crop);
  
 * Production quantities based on smaller area (before unhandledCropArea adjustment applied)
  totalProd(crop) = sum(location, area.l(crop, location) * yield.l(crop, location));
@@ -244,9 +244,14 @@ $gdxin
  totalCropland(location) = sum(crop_less_pasture, area.l(crop_less_pasture, location));
  totalArea(crop) = sum(location, area.l(crop, location));
  
+ feedCostRate(feed_crop)$[totalProd(feed_crop) <> 0] = totalProdCost(feed_crop) / totalProd(feed_crop);
+ totalProdCost('ruminants') = sum(feed_crop, ruminantFeed.l(feed_crop) * feedCostRate(feed_crop));
+ totalProdCost('monogastrics') = sum(feed_crop, monogastricFeed.l(feed_crop) * feedCostRate(feed_crop));
+ 
  netImportCost(import_crop) = importAmount.l(import_crop) * importPrices(import_crop) - exportAmount.l(import_crop) * exportPrices(import_crop);
  netImportAmount(import_crop) = importAmount.l(import_crop) - exportAmount.l(import_crop);
          
  Scalar ms 'model status', ss 'solve status'; 
  ms=LAND_USE.modelstat;
  ss=LAND_USE.solvestat;
+ 
\ No newline at end of file
diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
index 14713281..5d4b01de 100644
--- a/src/ac/ed/lurg/ModelConfig.java
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -163,6 +163,7 @@ public class ModelConfig {
 	public static final String LAND_COVER_OUTPUT_FILE = OUTPUT_DIR + File.separator + "lc.txt";
 	public static final String PRICES_OUTPUT_FILE = OUTPUT_DIR + File.separator + "prices.txt";
 	public static final String DEMAND_OUTPUT_FILE = OUTPUT_DIR + File.separator + "demand.txt";
+	public static final String DOMESTIC_OUTPUT_FILE = OUTPUT_DIR + File.separator + "domestic.txt";
 	public static final boolean OUTPUT_FOR_LPJG = getBooleanProperty("OUTPUT_FOR_LPJG", true);
 	public static final boolean INTERPOLATE_OUTPUT_YEARS = getBooleanProperty("INTERPOLATE_OUTPUT_YEARS", true);
 	
@@ -238,7 +239,14 @@ public class ModelConfig {
 	public static final String BIOENERGY_DEMAND_SCENARIO = getProperty("BIOENERGY_DEMAND_SCENARIO", "BAU");
 	public static final double BIOENERGY_DEMAND_SHIFT = IS_CALIBRATION_RUN ? 1.0 : getDoubleProperty("BIOENERGY_DEMAND_SHIFT", 1.0);
 //	public static final double BIOENERGY_HEATING_VALUE_GJ_PER_T = getDoubleProperty("BIOENERGY_HEATING_VALUE_GJ_PER_T", 17.5); // GJ per t DM
-
+	
+	//Dietary stuff
+	public static final boolean ACTIVE_DIETARY_SHIFT = getBooleanProperty("ACTIVE_DIETARY_SHIFT", false);
+	public static final int DIETARY_CHANGE_START_YEAR  = getIntProperty("DIETARY_CHANGE_START_YEAR", 2010);
+	public static final int DIETARY_CHANGE_END_YEAR  = getIntProperty("DIETARY_CHANGE_END_YEAR", 2050);
+	public static final double DIETARY_CHANGE_ANNUAL_RATE  = getDoubleProperty("DIETARY_CHANGE_ANNUAL_RATE", 0.05);
+	
+	
 	public static final double MARKET_LAMBA = getDoubleProperty("MARKET_LAMBA", 0.4); // controls international market price adjustment rate
 
 	public static final double POPULATION_AGGREG_LIMIT = getDoubleProperty("POPULATION_AGGREG_LIMIT", 30.0);  // in millions, smaller countries are aggregated on a regional basis
@@ -274,4 +282,6 @@ public class ModelConfig {
 	
 	public static final int NUM_YIELD_CLUSTERS = getIntProperty("NUM_YIELD_CLUSTERS", 5000);
 	public static final long RANDOM_SEED = getIntProperty("RANDOM_SEED", 1974329);  // any number will do
+	
+
 }
diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java
index 37dbdd75..05206b69 100644
--- a/src/ac/ed/lurg/ModelMain.java
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -150,7 +150,7 @@ public class ModelMain {
 		double gen2Increase = (gen2EcDDemand>previousGen2EcDDemand) ? gen2EcDDemand - previousGen2EcDDemand : 0.0;
 		CropToDoubleMap totalImportCommodities = new CropToDoubleMap();
 		CropToDoubleMap totalExportCommodities = new CropToDoubleMap();
-
+	
 		for (CountryAgent ca : countryAgents) {
 
 			LogWriter.println("Country " + ca.getCountry());
@@ -206,6 +206,7 @@ public class ModelMain {
 				else
 					totalExportCommodities.incrementValue(c, -countryNetImports);
 			}
+			
 		}
 
 		// energycrops are a special case where demand in global and exogenously specified
@@ -324,12 +325,51 @@ public class ModelMain {
 			LogWriter.print(e);
 		}
 	}
+	
+	
+	private void writeDomesticProductionFile(Timestep timestep) {
+		try {
+			StringBuffer sbHeadings = new StringBuffer("Year, Country, Crop,Production cost, export price, import price");
+			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.DOMESTIC_OUTPUT_FILE,
+					sbHeadings.toString());
+
+			for (CropType crop : CropType.getAllItems()) {
+				for (CountryAgent country : countryAgents) {
+					LogWriter.println("Country " + country.getCountry().getName() + " crop " + crop);
+					Double importPrice;
+					Double exportPrice;
+					Double prodCosts = country.getDomesticProductionCosts().get(crop);
+
+					if (crop.isImportedCrop()) {
+						importPrice = country.getCurrentCountryPrices().get(crop).getImportPrice();
+						exportPrice = country.getCurrentCountryPrices().get(crop).getExportPrice();
+					} else {
+						importPrice = null;
+						exportPrice = null;
+					}
+					StringBuffer sbData = new StringBuffer();
+					sbData.append(String.format("%d,%s, %s", timestep.getYear(), country.getCountry(),crop.getGamsName()));
+					sbData.append(String.format(",%.3f,%.3f,%.3f", prodCosts, importPrice, exportPrice));
+
+					outputFile.write(sbData.toString());
+					outputFile.newLine();
+				}
+
+			}
+			outputFile.close();
+
+		} catch (IOException e) {
+			LogWriter.print(e);
+		}
+
+	}
 
 	private void outputTimestepResults(Timestep timestep, RasterSet<LandUseItem> landUseRaster, YieldRaster yieldSurfaces) {
 
 		writeLandCoverFile(timestep, landUseRaster);
 		writeGlobalMarketFile(timestep);
 		writeDemandFile(timestep);
+		writeDomesticProductionFile(timestep);
 
 		if (ModelConfig.OUTPUT_FOR_LPJG) {
 			for (int outputYear : timestep.getYearsFromLast()) {
@@ -374,6 +414,8 @@ public class ModelMain {
 		// outputLandCover(timestep.getYear(), landUseRaster, LandCoverType.PASTURE);
 	}
 
+
+
 	private void outputWaterAvailablity(Timestep timestep, IrrigationRasterSet irrigiationRS) {
 		new RasterOutputer<Double, IrrigationItem>(irrigiationRS, ModelConfig.OUTPUT_DIR + File.separator + timestep.getYear() + File.separator + "IrrigConstraint.asc") {
 			@Override
@@ -528,7 +570,6 @@ public class ModelMain {
 			areasItem.setProtectedArea(landCover.getProtectedArea());
 			landUseRaster.put(key, areasItem);
 		}
-
 		return landUseRaster;
 	}
 
@@ -566,4 +607,5 @@ public class ModelMain {
 		initialStockLevels.put(CropType.ENERGY_CROPS, 0.0);
 		return initialStockLevels;
 	}
+	
 }
diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
index abaa9f9a..12292c04 100644
--- a/src/ac/ed/lurg/country/CountryAgent.java
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -41,6 +41,7 @@ public class CountryAgent {
 	private Map<CropType, CountryPrice> currentCountryPrices;
 	private Map<CropType, Double> tradeBarriers;
 	private RasterSet<IntegerRasterItem> yieldClusters;
+	private Map<CropType, Double> domesticProductionCosts;
 	
 	public CountryAgent(AbstractDemandManager demandManager,CompositeCountry country, RasterSet<LandUseItem> cropAreaRaster,
 			Map<CropType, CropUsageData> cropUsageData, Map<CropType, Double> tradeBarriers, RasterSet<IntegerRasterItem> yieldClusters) {
@@ -52,6 +53,7 @@ public class CountryAgent {
 		
 		GamsRasterOutput initialData = new GamsRasterOutput(cropAreaRaster, cropUsageData);
 		previousGamsRasterOutput = initialData;
+	
 	}
 	
 	public CompositeCountry getCountry() {
@@ -125,7 +127,10 @@ public class CountryAgent {
 			LogWriter.println("Running " + country.getName() + ", currentTimestep " + currentTimestep);
 			
 			GamsRasterOutput result = opti.run();
+			domesticProductionCosts = calculateDomesticProductionCosts(result);
+			
 			previousGamsRasterOutput = result;
+			
 			return result;
 		}
 		
@@ -135,6 +140,10 @@ public class CountryAgent {
 	public Map<CommodityType, Double> getCurrentProjectedDemand() {
 		return currentProjectedDemand;
 	}
+	
+	public Map<CropType, CountryPrice> getCurrentCountryPrices() {
+		return currentCountryPrices;
+	}
 
 	private GamsRasterInput getGamsRasterInput(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces, double gen2EcIncrease) {
 		double allowedImportChange;
@@ -191,4 +200,19 @@ public class CountryAgent {
 	
 		return countryPrices;
 	}
+	
+	public Map<CropType, Double> getDomesticProductionCosts() {
+		return domesticProductionCosts;
+	}
+	
+	private Map<CropType, Double> calculateDomesticProductionCosts(GamsRasterOutput result) {
+		
+		Map<CropType, Double> domesticProdCosts = new HashMap<CropType, Double>();
+		Map <CropType,  CropUsageData> cropUsage = result.getCropUsageData();
+		
+		for (Entry<CropType,  CropUsageData> entry : cropUsage.entrySet()) {
+			domesticProdCosts.put(entry.getKey(),entry.getValue().getProdCost());
+		}
+		return domesticProdCosts;
+	}
 }
diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
index fc385087..515ee338 100644
--- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
@@ -364,10 +364,10 @@ public class GamsLocationOptimiser {
 		for (CropType meatTypes : CropType.getMeatTypes()) {
 			netImport = getParmValue(parmNetImports, meatTypes.getGamsName());
 			prod = getParmValue(parmProd, meatTypes.getGamsName());
-//			prodCost = getParmValue(parmProdCost, meatTypes.getGamsName());  currently not calculated in GAMS correctly, not sure it's needed
+			prodCost = getParmValue(parmProdCost, meatTypes.getGamsName()); // currently not calculated in GAMS correctly, not sure it's needed
 			
-			cropUsageData.put(meatTypes, new CropUsageData(0.0, 0.0, netImport, prod, Double.NaN));
-			if (DEBUG) LogWriter.println(String.format("\n%s:\t\t\t\t\tnetImports= %.3f,\tprod= %.3f", meatTypes.getGamsName(), netImport, prod)); 
+			cropUsageData.put(meatTypes, new CropUsageData(0.0, 0.0, netImport, prod, prodCost));
+			if (DEBUG) LogWriter.println(String.format("\n%s:\t\t\t\t\tnetImports= %.3f,\tprod= %.3f,\tprodCost= %.3f", meatTypes.getGamsName(), netImport, prod, prodCost)); 
 		}
 		LogWriter.println(String.format("\n%s %s: Total area= %.1f (crop=%.1f, pasture %.1f)", 
 				inputData.getCountryInput().getCountry(), inputData.getTimestep().getYear(), 
diff --git a/src/ac/ed/lurg/demand/AbstractDemandManager.java b/src/ac/ed/lurg/demand/AbstractDemandManager.java
index d18383ee..4efe551d 100644
--- a/src/ac/ed/lurg/demand/AbstractDemandManager.java
+++ b/src/ac/ed/lurg/demand/AbstractDemandManager.java
@@ -7,8 +7,10 @@ import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.Timestep;
 import ac.ed.lurg.country.CompositeCountry;
 import ac.ed.lurg.country.CompositeCountryManager;
+import ac.ed.lurg.country.GlobalPrice;
 import ac.ed.lurg.country.SingleCountry;
 import ac.ed.lurg.types.CommodityType;
+import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.utils.LogWriter;
 
 public abstract class AbstractDemandManager {
@@ -18,37 +20,45 @@ public abstract class AbstractDemandManager {
 
 	public AbstractDemandManager(CompositeCountryManager compositeCountryManager) {
 		this.compositeCountryManager = compositeCountryManager;
-		bioenergyDemandManager  = new BioenergyDemandManager();
+		bioenergyDemandManager = new BioenergyDemandManager();
 	}
 
 	public Map<CommodityType, Double> getDemand(CompositeCountry cc, int year) {
 
-		if (!ModelConfig.CHANGE_DEMAND_YEAR) 
+		if (!ModelConfig.CHANGE_DEMAND_YEAR)
 			year = ModelConfig.BASE_YEAR;
 
 		Map<CommodityType, Double> demandMap = new HashMap<CommodityType, Double>();
-				
+		Map<CommodityType, Double> foodDemandMap;
+
 		for (SingleCountry c : compositeCountryManager.getAllForCompositeCountry(cc)) {
-			Map<CommodityType, Double> foodDemandMap = getFoodDemand(c, year);
+
+			if (ModelConfig.ACTIVE_DIETARY_SHIFT) {
+				foodDemandMap = calculateDietaryShift(c, year);
+			} else
+				foodDemandMap = getFoodDemand(c, year);
 
 			for (CommodityType commodity : CommodityType.getAllItems()) {
+
 				Double food = foodDemandMap.get(commodity);
 				double bioenergy = bioenergyDemandManager.getFirstGenBioenergyDemand(c, year, commodity);
-				//		LogWriter.println(String.format("Country %s comm %s: %f",  country, commodity, bioenergy));
+				// LogWriter.println(String.format("Country %s comm %s: %f",
+				// country, commodity, bioenergy));
 
-				double newDemand = ((food==null ? 0.0 : food.doubleValue()) + bioenergy) * (1.0 + commodity.getSeedWasteRate());
+				double newDemand = ((food == null ? 0.0 : food.doubleValue()) + bioenergy)
+						* (1.0 + commodity.getSeedWasteRate());
 				Double prevDemand = demandMap.get(commodity);
 
 				if (prevDemand != null)
-					newDemand += prevDemand.doubleValue();  // aggregate if composite country
+					newDemand += prevDemand.doubleValue(); // aggregate if
+															// composite country
 
 				demandMap.put(commodity, newDemand);
 			}
 		}
-
 		return demandMap;
 	}
-	
+
 	abstract Map<CommodityType, Double> getFoodDemand(SingleCountry c, int year);
 
 	public double getSecondGenBioenergyDemand(Timestep timestep) {
@@ -57,4 +67,48 @@ public abstract class AbstractDemandManager {
 		LogWriter.println("Global gen2 bioenergy demand in " + timestep + " is " + d);
 		return d;
 	}
+
+	private Map<CommodityType, Double> calculateDietaryShift(SingleCountry c, int year) {
+
+		int yearsOfChange = Math.min(ModelConfig.DIETARY_CHANGE_END_YEAR - ModelConfig.DIETARY_CHANGE_START_YEAR,
+				year - ModelConfig.DIETARY_CHANGE_START_YEAR);
+		double dietaryDemandAdj = yearsOfChange > 0 ? (yearsOfChange * ModelConfig.DIETARY_CHANGE_ANNUAL_RATE) : 0.0;
+
+		Map<CommodityType, Double> originalFoodDemandMap = getFoodDemand(c, year);
+
+		Double monogastricDemand = originalFoodDemandMap.get(CommodityType.MONOGASTRICS);
+		double mongoastricCalorieShortfall = (monogastricDemand != null)
+				? monogastricDemand * dietaryDemandAdj * CommodityType.MONOGASTRICS.getEnergyPerTon() : 0.0;
+
+		Double ruminantDemand = originalFoodDemandMap.get(CommodityType.RUMINANTS);
+		double ruminantsCalorieShortfall = (ruminantDemand != null)
+				? ruminantDemand * dietaryDemandAdj * CommodityType.RUMINANTS.getEnergyPerTon() : 0.0;
+
+		Map<CommodityType, Double> updatedFoodDemandMap = new HashMap<CommodityType, Double>();
+
+		for (CommodityType commodity : CommodityType.getAllItems()) {
+			Double originalCommodityDemand = originalFoodDemandMap.get(commodity);
+
+			if (originalCommodityDemand != null) {
+				if (commodity.isAnimalProduct()) {
+					updatedFoodDemandMap.put(commodity,
+							originalCommodityDemand - originalCommodityDemand * dietaryDemandAdj);
+				} else {
+					updatedFoodDemandMap.put(commodity,
+							originalCommodityDemand
+									+ commodity.getMeatSubstitutionProportion() * ruminantsCalorieShortfall
+											/ commodity.getEnergyPerTon()
+									+ commodity.getMeatSubstitutionProportion() * mongoastricCalorieShortfall
+											/ commodity.getEnergyPerTon());
+
+				}
+			}
+
+		}
+
+//		LogWriter.println("original map " + originalFoodDemandMap);
+//		LogWriter.println("updated map " + updatedFoodDemandMap);
+		return updatedFoodDemandMap;
+	}
+
 }
diff --git a/src/ac/ed/lurg/types/CommodityType.java b/src/ac/ed/lurg/types/CommodityType.java
index 615b6efb..b66ff622 100644
--- a/src/ac/ed/lurg/types/CommodityType.java
+++ b/src/ac/ed/lurg/types/CommodityType.java
@@ -8,21 +8,25 @@ import ac.ed.lurg.ModelConfig;
 
 public enum CommodityType {
 
-	CEREALS("Cereals", "cereals", false),
-	OILCROPS("Oilcrops", "oilcrops", false),
-	PULSES("Pulses", "pulses", false),
-	STARCHY_ROOTS("Starchy Roots", "starchyRoots", false),
-	MONOGASTRICS("Monogastrics", "monogastrics", true),
-	RUMINANTS("Ruminants", "ruminants", true);
+	CEREALS("Cereals", "cereals", false, 0.9393854,0.2),
+	OILCROPS("Oilcrops", "oilcrops", false, 2.0640544,0.1),
+	PULSES("Pulses", "pulses", false, 2.5886803,0.4),
+	STARCHY_ROOTS("Starchy Roots", "starchyRoots", false, 0.4925546,0.3),
+	MONOGASTRICS("Monogastrics", "monogastrics", true, 0.4680600, 1.0),
+	RUMINANTS("Ruminants", "ruminants", true, 0.6135583, 1.0);
 
 	private String faoName;
 	private String gamsName;
 	private boolean isAnimalProduct;
+	private double energyPerTon;
+	private double meatSubstitutionProportion;
 	
-	CommodityType (String faoName, String gamsName, boolean isAnimalProduct) {
+	CommodityType (String faoName, String gamsName, boolean isAnimalProduct, double energyPerTon, double meatSubstitutionProportion) {
 		this.faoName = faoName;
 		this.gamsName = gamsName;
 		this.isAnimalProduct = isAnimalProduct;
+		this.energyPerTon = energyPerTon;
+		this.meatSubstitutionProportion = meatSubstitutionProportion;
 	}
 	
 	private static final Map<String, CommodityType> faoNameCache = new HashMap<String, CommodityType>();
@@ -57,6 +61,14 @@ public enum CommodityType {
 		return isAnimalProduct;
 	}
 	
+	public double getEnergyPerTon(){
+		return energyPerTon;
+	}
+	
+	public double getMeatSubstitutionProportion(){
+		return meatSubstitutionProportion;
+	}
+	
 	public double getSeedWasteRate() {
 		// just waste rate for animal products
 		return isAnimalProduct ? ModelConfig.SEED_AND_WASTE_FRACTION/2 : ModelConfig.SEED_AND_WASTE_FRACTION;
-- 
GitLab