From f313e8d6ea282dea4e736433ee08904a2863a8d3 Mon Sep 17 00:00:00 2001
From: Peter Alexander <peter@blackhillock.co.uk>
Date: Thu, 14 Mar 2019 08:16:36 +0000
Subject: [PATCH] Use single price markup factor per commodity between demand
 and production prices

---
 src/ac/ed/lurg/InternationalMarket.java       | 20 ++--------
 .../ed/lurg/country/AbstractCountryAgent.java | 40 +++++++------------
 src/ac/ed/lurg/types/CommodityType.java       | 37 +++++++++++------
 src/ac/ed/lurg/types/CropType.java            | 34 +++++++++-------
 4 files changed, 63 insertions(+), 68 deletions(-)

diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java
index c9cd1cbc..4504439a 100644
--- a/src/ac/ed/lurg/InternationalMarket.java
+++ b/src/ac/ed/lurg/InternationalMarket.java
@@ -17,25 +17,13 @@ import ac.ed.lurg.utils.LogWriter;
 
 public class InternationalMarket {
 
-	private Map<CropType, GlobalPrice> worldPrices;
+	private Map<CropType, GlobalPrice> worldPrices = new HashMap<CropType, GlobalPrice>();
 	private Map<CropType, Double> stockLevel;
 
 	public InternationalMarket() {
-		// in first timestep we don't have this info, but ok as constrained to
-		// import/export specified amount, values based on
-		// http://www.indexmundi.com/commodities/ for Jun 2010
-		worldPrices = new HashMap<CropType, GlobalPrice>();
-		worldPrices.put(CropType.WHEAT, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_WHEAT));
-		worldPrices.put(CropType.MAIZE, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_MAIZE));
-		worldPrices.put(CropType.RICE, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_RICE));
-		worldPrices.put(CropType.OILCROPS, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_OILCROPS));
-		worldPrices.put(CropType.PULSES, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_PULSES));
-		worldPrices.put(CropType.STARCHY_ROOTS, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_STARCHYROOTS));
-		worldPrices.put(CropType.MONOGASTRICS, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_MONOGASTRICS)); // quantities is in feed equivalent term (0.4 is weighted average price per feed, and 0.5 accounts for mark-up for additional processing)
-		worldPrices.put(CropType.RUMINANTS, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_RUMINANTS)); // quantities is in feed equivalent term
-		worldPrices.put(CropType.ENERGY_CROPS, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_ENERGYCROPS));
-		worldPrices.put(CropType.FRUITVEG, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_FRUITVEG));
-		worldPrices.put(CropType.SUGAR, GlobalPrice.createInitial(ModelConfig.INITAL_PRICE_SUGAR));
+		for (CropType crop : CropType.getImportedTypes())
+			worldPrices.put(crop, GlobalPrice.createInitial(crop.getInitialPrice()));
+		
 		stockLevel = getInitialStockLevels();
 	}
 	
diff --git a/src/ac/ed/lurg/country/AbstractCountryAgent.java b/src/ac/ed/lurg/country/AbstractCountryAgent.java
index 89cb3ded..0f2bb225 100644
--- a/src/ac/ed/lurg/country/AbstractCountryAgent.java
+++ b/src/ac/ed/lurg/country/AbstractCountryAgent.java
@@ -1,6 +1,5 @@
 package ac.ed.lurg.country;
 
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -20,7 +19,6 @@ public abstract class AbstractCountryAgent {
 	protected Map<CommodityType, Double> currentProjectedDemand;
 	private Map<CropType, CountryPrice> currentCountryPrices;
 	private Map<CommodityType, Map<CropType, Double>> baseDemandFact;
-	private Map<CommodityType, Double> priceMarkupFactors = new HashMap<CommodityType, Double>();
 
 	public AbstractCountryAgent(AbstractDemandManager demandManager,CompositeCountry country, Map<CropType, Double> tradeBarriers) {
 
@@ -40,6 +38,7 @@ public abstract class AbstractCountryAgent {
 		for (CropType c : CropType.getImportedTypes()) {
 			GlobalPrice worldPrice = worldPrices.get(c);
 			Double tb = tradeBarriers.get(c);
+			LogWriter.println(worldPrice + " " + c);
 			CountryPrice prices = new CountryPrice(worldPrice.getCountryImportPrice(tb==null ? 0 : tb, timestep), worldPrice.getExportPrice());
 			countryPrices.put(c, prices);
 		}
@@ -62,34 +61,25 @@ public abstract class AbstractCountryAgent {
 		Map<CommodityType, Double> prices = new HashMap<CommodityType, Double>();
 		
 		for (CommodityType commodity : CommodityType.getAllFoodItems()) {
-			double commPricePlum = 0;
-			Collection<CropType> crops = commodity.getCropTypes();
+			double commPricePlum = getCommPriceFromCropPrice(commodity);
+			double commPrice = commPricePlum * commodity.getPlumPriceToKcalPriceConversion();  //price per person per year commodity x per year assuming 2000 kcal a day
+			commPrice *= commodity.getPriceMarkupFactor(); // * priceMarkupFactor to rebase prices to those provide by G&G
 			
-			for (CropType crop : crops) {
-				commPricePlum += getCropPrice(crop) * baseDemandFact.get(commodity).get(crop);  // weight price by base demand of each cereal crop
-			}
-			
-			double commPrice = commPricePlum * 1000 / commodity.getkcalPerT() * 2000 * 365;  //price per person per year commodity x per year assuming 2000 kcal a day
-
-			double priceMarkupFactor;
-			if (timestep.isInitialTimestep()) {
-				double initialDemandPrice = commodity.getInitialPrice();
-				priceMarkupFactor = initialDemandPrice / commPrice;
-				priceMarkupFactors.put(commodity, priceMarkupFactor);
-			}
-			else
-				priceMarkupFactor = priceMarkupFactors.get(commodity);
-			
-			prices.put(commodity, commPrice * priceMarkupFactor);   // * priceMarkupFactor to rebase prices to those provide by G&G
-			LogWriter.println("Price for " + commodity.getGamsName() + " is " + commPrice * priceMarkupFactor + " after markup of " + priceMarkupFactor);
+			prices.put(commodity, commPrice);
+			LogWriter.println("Price for " + commodity.getGamsName() + " is " + commPrice);
 		}
 		
 		return prices;
 	}
-
-	protected double getCropPrice(CropType crop) {
-		CountryPrice cropPrice = currentCountryPrices.get(crop);
-		return cropPrice.getImportPrice();
+	
+	private double getCommPriceFromCropPrice(CommodityType commodity) {
+		double commPricePlum = 0;
+		Map<CropType, Double> demandFract = baseDemandFact.get(commodity);
+		
+		for (CropType crop : commodity.getCropTypes()) {
+			commPricePlum += currentCountryPrices.get(crop).getImportPrice() * demandFract.get(crop);  // weight price by base demand of each cereal crop
+		}
+		return commPricePlum;
 	}
 
 	public Map<CommodityType, Double> getCurrentProjectedDemand() {
diff --git a/src/ac/ed/lurg/types/CommodityType.java b/src/ac/ed/lurg/types/CommodityType.java
index 0922347d..37887815 100644
--- a/src/ac/ed/lurg/types/CommodityType.java
+++ b/src/ac/ed/lurg/types/CommodityType.java
@@ -7,17 +7,18 @@ import java.util.HashSet;
 import java.util.Map;
 
 import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.utils.LogWriter;
 
 public enum CommodityType {
 
-	CEREALS("Cereals", "cereals", false, new CropType[]{CropType.WHEAT, CropType.MAIZE, CropType.RICE},2741847.3, ModelConfig.CEREALS_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_CEREALS),
-	OILCROPSPULSES("OilcropsPulses", "oilcropspulses", false, new CropType[]{CropType.OILCROPS, CropType.PULSES},1864622.3, ModelConfig.OILCROPSPULSES_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_OILCROPS_PULSES),
-	STARCHY_ROOTS("Starchy Roots", "starchyRoots", false, new CropType[]{CropType.STARCHY_ROOTS},819065.4, ModelConfig.STARCHY_ROOTS_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_STARCHYROOTS),
-	MONOGASTRICS("Monogastrics", "monogastrics", true, new CropType[]{CropType.MONOGASTRICS}, 455874.3, Double.NaN, ModelConfig.INITAL_DEMAND_PRICE_MONOGASTRICS),
-	RUMINANTS("Ruminants", "ruminants", true, new CropType[]{CropType.RUMINANTS}, 236969.8, Double.NaN, ModelConfig.INITAL_DEMAND_PRICE_RUMINANTS),
-	FRUITVEG("FruitVeg", "fruitveg", false, new CropType[]{CropType.FRUITVEG},334355.8, ModelConfig.FRUITVEG_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_FRUITVEG),
-	SUGAR("Sugar", "sugar", false, new CropType[]{CropType.SUGAR},3417804.4, ModelConfig.SUGAR_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_SUGAR),
-	NONFOOD("Nonfood", "nonfood", false, new CropType[]{},Double.NaN, Double.NaN, Double.NaN);
+	CEREALS("Cereals", "cereals", false, new CropType[]{CropType.WHEAT, CropType.MAIZE, CropType.RICE}, new Double[]{0.4, 0.3, 0.3}, 2741847.3, ModelConfig.CEREALS_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_CEREALS),
+	OILCROPSPULSES("OilcropsPulses", "oilcropspulses", false, new CropType[]{CropType.OILCROPS, CropType.PULSES}, new Double[]{0.5, 0.5}, 1864622.3, ModelConfig.OILCROPSPULSES_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_OILCROPS_PULSES),
+	STARCHY_ROOTS("Starchy Roots", "starchyRoots", false, new CropType[]{CropType.STARCHY_ROOTS}, new Double[]{1.0}, 819065.4, ModelConfig.STARCHY_ROOTS_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_STARCHYROOTS),
+	MONOGASTRICS("Monogastrics", "monogastrics", true, new CropType[]{CropType.MONOGASTRICS}, new Double[]{1.0}, 455874.3, Double.NaN, ModelConfig.INITAL_DEMAND_PRICE_MONOGASTRICS),
+	RUMINANTS("Ruminants", "ruminants", true, new CropType[]{CropType.RUMINANTS}, new Double[]{1.0}, 236969.8, Double.NaN, ModelConfig.INITAL_DEMAND_PRICE_RUMINANTS),
+	FRUITVEG("FruitVeg", "fruitveg", false, new CropType[]{CropType.FRUITVEG}, new Double[]{1.0}, 334355.8, ModelConfig.FRUITVEG_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_FRUITVEG),
+	SUGAR("Sugar", "sugar", false, new CropType[]{CropType.SUGAR}, new Double[]{1.0}, 3417804.4, ModelConfig.SUGAR_SUB_PROPORTION, ModelConfig.INITAL_DEMAND_PRICE_SUGAR),
+	NONFOOD("Nonfood", "nonfood", false, new CropType[]{}, new Double[]{}, Double.NaN, Double.NaN, Double.NaN);
 
 	private String faoName;
 	private String gamsName;
@@ -25,9 +26,9 @@ public enum CommodityType {
 	private Collection<CropType> cropTypes;
 	private double kcalPerT; //in feed equivalent for animal types
 	private double meatSubstitutionProportion;
-	private double initialPrice;
+	private double priceMarkupFactor;
 	
-	CommodityType (String faoName, String gamsName, boolean isAnimalProduct, CropType[] cropTypeMapping, double energyPerTon, 
+	CommodityType (String faoName, String gamsName, boolean isAnimalProduct, CropType[] cropTypeMapping, Double[] defaultDemandFract, double energyPerTon, 
 			double meatSubstitutionProportion, double initialPrice) {
 		this.faoName = faoName;
 		this.gamsName = gamsName;
@@ -35,7 +36,13 @@ public enum CommodityType {
 		this.cropTypes = Arrays.asList(cropTypeMapping);
 		this.kcalPerT = energyPerTon; 
 		this.meatSubstitutionProportion = meatSubstitutionProportion;
-		this.initialPrice = initialPrice;
+		
+		double prodPrice = 0;
+		for (int i=0; i<cropTypeMapping.length; i++)
+			prodPrice += cropTypeMapping[i].getInitialPrice() * defaultDemandFract[i];
+
+		priceMarkupFactor = initialPrice / (prodPrice * getPlumPriceToKcalPriceConversion());
+		LogWriter.println("priceMarkupFactor for " + gamsName + " is " + priceMarkupFactor);
 	}
 	
 	private static final Map<String, CommodityType> faoNameCache = new HashMap<String, CommodityType>();
@@ -97,7 +104,11 @@ public enum CommodityType {
 		return cropTypes;
 	}
 
-	public double getInitialPrice() {
-		return initialPrice;
+	public double getPriceMarkupFactor() {
+		return priceMarkupFactor;
+	}
+	
+	public double getPlumPriceToKcalPriceConversion() {
+		return 1000 / getkcalPerT() * 2000 * 365;
 	}
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/types/CropType.java b/src/ac/ed/lurg/types/CropType.java
index 7d64a86a..ec0241d7 100644
--- a/src/ac/ed/lurg/types/CropType.java
+++ b/src/ac/ed/lurg/types/CropType.java
@@ -6,38 +6,41 @@ import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Map;
 
+import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.utils.LogWriter;
 
 public enum CropType {
 
-	WHEAT("WheatBarleyOats", "wheat", true, false, 9.4),
-	MAIZE("MaizeMilletSorghum", "maize", true, false, 5.1),
-	RICE("Rice (Paddy Equivalent)", "rice", true, false, 8.4),
-	OILCROPS("Oilcrops", "oilcrops", true, false, 4.4),
-	PULSES("Pulses", "pulses", true, false, 11.2),
-	STARCHY_ROOTS("Starchy Roots", "starchyRoots", true, false, 14.2),
-	ENERGY_CROPS("Energy crops", "energycrops", true, false, 5),
-	SETASIDE("setaside", "setaside", false, false, 0),
-	MONOGASTRICS("Monogastrics", "monogastrics", true, true, 3.1),
-	RUMINANTS("Ruminants", "ruminants", true, true, 2.2),
-	FRUITVEG("FruitVeg", "fruitveg", true, false, 8.8),
-	SUGAR("Sugar", "sugar", true, false, 0.1),
-	PASTURE("pasture", "pasture", false, false, 0);  // confusing having a land cover and a crop type.  Needed here for yields (but not used for cropland area fractions).
+	WHEAT("WheatBarleyOats", "wheat", true, false, 9.4, ModelConfig.INITAL_PRICE_WHEAT),
+	MAIZE("MaizeMilletSorghum", "maize", true, false, 5.1, ModelConfig.INITAL_PRICE_MAIZE),
+	RICE("Rice (Paddy Equivalent)", "rice", true, false, 8.4, ModelConfig.INITAL_PRICE_RICE),
+	OILCROPS("Oilcrops", "oilcrops", true, false, 4.4, ModelConfig.INITAL_PRICE_OILCROPS),
+	PULSES("Pulses", "pulses", true, false, 11.2, ModelConfig.INITAL_PRICE_PULSES),
+	STARCHY_ROOTS("Starchy Roots", "starchyRoots", true, false, 14.2, ModelConfig.INITAL_PRICE_STARCHYROOTS),
+	ENERGY_CROPS("Energy crops", "energycrops", true, false, 5, ModelConfig.INITAL_PRICE_ENERGYCROPS),
+	SETASIDE("setaside", "setaside", false, false, 0, 0),
+	MONOGASTRICS("Monogastrics", "monogastrics", true, true, 3.1, ModelConfig.INITAL_PRICE_MONOGASTRICS),
+	RUMINANTS("Ruminants", "ruminants", true, true, 2.2, ModelConfig.INITAL_PRICE_RUMINANTS),
+	FRUITVEG("FruitVeg", "fruitveg", true, false, 8.8, ModelConfig.INITAL_PRICE_FRUITVEG),
+	SUGAR("Sugar", "sugar", true, false, 0.1, ModelConfig.INITAL_PRICE_SUGAR),
+	PASTURE("pasture", "pasture", false, false, 0, 0);  // confusing having a land cover and a crop type.  Needed here for yields (but not used for cropland area fractions).
 
 	private String faoName;
 	private String gamsName;
 	private boolean importedCrop;
 	private boolean isMeat;
 	private double seedAndWasteRate;
+	private double initialPrice;
 	
 	private static Collection<CropType> importedCrops;
 
-	private CropType (String faoName, String gamsName, boolean importedCrop, boolean isMeat, double seedAndWastePercent) {
+	private CropType (String faoName, String gamsName, boolean importedCrop, boolean isMeat, double seedAndWastePercent, double initialPrice) {
 		this.faoName = faoName;
 		this.gamsName = gamsName;
 		this.importedCrop = importedCrop;
 		this.isMeat = isMeat;
 		this.seedAndWasteRate = seedAndWastePercent/100;
+		this.initialPrice = initialPrice;
 	}
 	
 	public static Collection<CropType> getCropsLessPasture() {
@@ -133,4 +136,7 @@ public enum CropType {
 		return seedAndWasteRate;
 	}
 
+	public double getInitialPrice() {
+		return initialPrice;
+	}
 }
-- 
GitLab