From 6095d0774526269f6c5a94a2f4a354dd9eadecd2 Mon Sep 17 00:00:00 2001
From: Peter Alexander <peter@blackhillock.co.uk>
Date: Sun, 11 Nov 2018 12:07:04 +0000
Subject: [PATCH] Allow setting of subsidy rates

---
 GAMS/IntExtOpt.gms                            |  5 +-
 GAMS/LUOpt.gms                                |  5 +-
 data/subsidyrates.csv                         |  1 +
 src/ac/ed/lurg/ModelConfig.java               |  4 +-
 src/ac/ed/lurg/ModelMain.java                 | 11 +++-
 src/ac/ed/lurg/country/CountryAgent.java      |  7 +-
 .../ed/lurg/country/SubsidyRateManager.java   | 66 +++++++++++++++++++
 .../lurg/country/gams/GamsCountryInput.java   |  8 ++-
 .../country/gams/GamsLocationOptimiser.java   |  8 +++
 src/ac/ed/lurg/demand/DemandCurve.java        |  1 -
 10 files changed, 104 insertions(+), 12 deletions(-)
 create mode 100644 data/subsidyrates.csv
 create mode 100644 src/ac/ed/lurg/country/SubsidyRateManager.java

diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms
index 125271f9..c02f4ed5 100644
--- a/GAMS/IntExtOpt.gms
+++ b/GAMS/IntExtOpt.gms
@@ -34,6 +34,7 @@
  PARAMETER irrigConstraint(location)         max water available for irrigation in litre per m2;
  PARAMETER minDemandPerCereal(cereal_crop)   min demand for each cereal crop as factor of all cereals;
  PARAMETER seedAndWasteRate(all_types)       rate of use for seed and waste combined;
+ PARAMETER subsidyRate(crop)                 rates of subsidy compared to costs;
  
  PARAMETER agriExpansionCost(location)       price for increase agricultural area varies based on amount of managed or unmanaged forest;
  SCALAR pastureIncCost                       price for increasing pasture area;
@@ -56,7 +57,7 @@ $gdxin %gdxincname%
 $load location, suitableLandArea, demand, agriExpansionCost, cropIncCost, pastureIncCost, cropDecCost, pastureDecCost
 $load previousArea, previousFertIntensity, previousIrrigIntensity, previousOtherIntensity, previousRuminantFeed, previousMonogastricFeed, previousImportAmount, previousExportAmount
 $load yieldNone, yieldFertOnly, yieldIrrigOnly, yieldBoth
-$load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxNetImport, minNetImport, unhandledCropRate, setAsideRate, maxLandExpansionRate
+$load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxNetImport, minNetImport, unhandledCropRate, setAsideRate, maxLandExpansionRate, subsidyRate
 $load meatEfficency, otherICost, irrigCost, irrigMaxRate, irrigConstraint, fertiliserUnitCost, domesticPriceMarkup, minDemandPerCereal, seedAndWasteRate, animalFeedFromOtherSources
 $gdxin    
  
@@ -203,7 +204,7 @@ $gdxin
 
  COST_EQ .. total_cost =E= 
          ( 
-              (   SUM((crop, location), area(crop, location) * unitCost(crop, location)) +
+              (   SUM((crop, location), area(crop, location) * unitCost(crop, location) * (1-subsidyRate(crop))) +
                   
                   SUM(location, 
                      agriExpansionCost(location) * agriLandExpansion(location) +
diff --git a/GAMS/LUOpt.gms b/GAMS/LUOpt.gms
index 16279db0..ad188039 100644
--- a/GAMS/LUOpt.gms
+++ b/GAMS/LUOpt.gms
@@ -35,6 +35,7 @@
  PARAMETER irrigConstraint(location)         max water available for irrigation in litre per m2;
  PARAMETER minDemandPerCereal(cereal_crop)   min demand for each cereal crop as factor of all cereals;
  PARAMETER seedAndWasteRate(all_types)       rate of use for seed and waste combined;
+ PARAMETER subsidyRate(crop)                 rates of subsidy compared to costs;
  
  PARAMETER agriExpansionCost(location)       price for increase agricultural area varies based on amount of managed or unmanaged forest;
  SCALAR pastureIncCost                       price for increasing pasture area;
@@ -57,7 +58,7 @@ $gdxin %gdxincname%
 $load location, suitableLandArea, demand, agriExpansionCost, cropIncCost, pastureIncCost, cropDecCost, pastureDecCost
 $load previousArea, previousFertIntensity, previousIrrigIntensity, previousOtherIntensity, previousRuminantFeed, previousMonogastricFeed, previousImportAmount
 $load yieldNone, yieldFertOnly, yieldIrrigOnly, yieldBoth
-$load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxProduction, minProduction, unhandledCropRate, setAsideRate, maxLandExpansionRate
+$load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxProduction, minProduction, unhandledCropRate, setAsideRate, maxLandExpansionRate, subsidyRate
 $load meatEfficency, otherICost, irrigCost, irrigMaxRate, irrigConstraint, fertiliserUnitCost, domesticPriceMarkup, minDemandPerCereal, seedAndWasteRate, animalFeedFromOtherSources
 
 $gdxin    
@@ -212,7 +213,7 @@ $gdxin
          ( 
               SUM(traded_commodity, domesticallyMetDemand(traded_commodity) * importPrices(traded_commodity) + export(traded_commodity) * exportPrices(traded_commodity)) - 
               
-              (   SUM((crop, location), area(crop, location) * unitCost(crop, location)) +
+              (   SUM((crop, location), area(crop, location) * unitCost(crop, location) * (1-subsidyRate(crop))) +
                   
                   SUM(location, 
                      agriExpansionCost(location) * agriLandExpansion(location) +
diff --git a/data/subsidyrates.csv b/data/subsidyrates.csv
new file mode 100644
index 00000000..40012d0a
--- /dev/null
+++ b/data/subsidyrates.csv
@@ -0,0 +1 @@
+country,crop,rate
diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java
index ef9bc1c5..4a743c1b 100644
--- a/src/ac/ed/lurg/ModelConfig.java
+++ b/src/ac/ed/lurg/ModelConfig.java
@@ -166,6 +166,7 @@ public class ModelConfig {
 	public static final String OTHER_WATER_USES_FILE = DATA_DIR + File.separator + "other_water_uses.csv";
 	public static final String BASE_CEREAL_DEMAND_FILE = DATA_DIR + File.separator + "base_cereal_demand.csv";
 	public static final String SHOCKS_PARAMETER_FILE = OUTPUT_DIR + File.separator+ "shocks.csv";
+	public static final String SUBSIDY_RATE_FILE = DATA_DIR + File.separator + "subsidyrates.csv";
 
 	// yield data
 	public static final String YIELD_DIR_BASE = getProperty("YIELD_DIR_BASE");
@@ -190,7 +191,6 @@ public class ModelConfig {
 	public static final double INITAL_PRICE_MAIZE = getDoubleProperty("INITAL_PRICE_MAIZE", 0.152 * ModelConfig.INITIAL_PRICE_SHIFT);
 	public static final double INITAL_PRICE_RICE = getDoubleProperty("INITAL_PRICE_RICE", 0.182 * ModelConfig.INITIAL_PRICE_SHIFT);
 	public static final double INITAL_PRICE_OILCROPS = getDoubleProperty("INITAL_PRICE_OILCROPS", (0.820 * .4 + 0.314 * .6) * 0.3 * ModelConfig.INITIAL_PRICE_SHIFT);
-
 	public static final double INITAL_PRICE_PULSES = getDoubleProperty("INITAL_PRICE_PULSES", 0.4 * ModelConfig.INITIAL_PRICE_SHIFT);
 	public static final double INITAL_PRICE_STARCHYROOTS = getDoubleProperty("INITAL_PRICE_STARCHYROOTS", 0.1 * ModelConfig.INITIAL_PRICE_SHIFT);
 	public static final double INITAL_PRICE_MONOGASTRICS = getDoubleProperty("INITAL_PRICE_MONOGASTRICS", 0.4 * 0.5 * ModelConfig.INITIAL_PRICE_SHIFT); // quantities is in feed equivalent term (0.4 is weighted average price per feed, and 0.5 accounts for mark-up for additional processing)
@@ -360,4 +360,6 @@ public class ModelConfig {
 	public static final boolean FORCE_PROTECTED_AREAS = IS_CALIBRATION_RUN ? false : getBooleanProperty("FORCE_PROTECTED_AREAS", false);
 	public static final int FORCE_PROTECTED_AREAS_START_YEAR  = getIntProperty("FORCE_PROTECTED_AREAS_START_YEAR", 2020);
 	public static final int FORCE_PROTECTED_AREAS_END_YEAR  = getIntProperty("FORCE_PROTECTED_AREAS_END_YEAR", 2050);
+	public static final String COUNTRY_FOR_SUBSIDY = getProperty("COUNTRY_FOR_SUBSIDY", "United States of America");
+	public static final double SUBSIDY_RATE = getDoubleProperty("SUBSIDY_RATE", 0.39);
 }
diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java
index ba088af1..273e11ba 100644
--- a/src/ac/ed/lurg/ModelMain.java
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -23,6 +23,7 @@ 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;
@@ -44,8 +45,8 @@ import ac.ed.lurg.landuse.ProtectedAreasReader;
 import ac.ed.lurg.landuse.RunOffReader;
 import ac.ed.lurg.output.LandUseOutputer;
 import ac.ed.lurg.output.LpjgOutputer;
-import ac.ed.lurg.shock.YieldShocks;
 import ac.ed.lurg.shock.YieldShockReader;
+import ac.ed.lurg.shock.YieldShocks;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.types.LandCoverType;
@@ -66,6 +67,7 @@ public class ModelMain {
 	private Collection<CountryAgent> countryAgents;
 	private CountryBoundaryRaster countryBoundaryRaster;
 	private AbstractDemandManager demandManager;
+	private SubsidyRateManager subsidyRateManager;
 	private TradeManager tradeManager;
 	private CompositeCountryManager compositeCountryManager;
 	private RasterHeaderDetails desiredProjection;
@@ -88,7 +90,8 @@ public class ModelMain {
 
 		BaseConsumpManager baseConsumpManager = new BaseConsumpManager();
 		compositeCountryManager = new CompositeCountryManager(baseConsumpManager);
-
+		subsidyRateManager = new SubsidyRateManager(compositeCountryManager);
+		
 		if (ModelConfig.DEMAND_FROM_FILE)
 			demandManager = new DemandManagerFromFile(compositeCountryManager);
 		else
@@ -487,8 +490,10 @@ private void writeDomesticProductionFile(Timestep timestep) {
 					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);
+				CountryAgent ca = new CountryAgent(demandManager, cc, initCountryLandUse, countryCommodityData, countryTradeBarriers, yieldClusters, subsidyRates);
 				countryAgents.add(ca);
 				LogWriter.println("Creating country agent for: " + cc);
 			}
diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
index 7319b107..142c5eb1 100644
--- a/src/ac/ed/lurg/country/CountryAgent.java
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -42,14 +42,17 @@ public class CountryAgent {
 	private Map<CropType, Double> tradeBarriers;
 	private RasterSet<IntegerRasterItem> yieldClusters;
 	private double animalFeedFromOtherSources;
+	private Map<CropType, Double> subsidyRates;
 
 	public CountryAgent(AbstractDemandManager demandManager,CompositeCountry country, RasterSet<LandUseItem> cropAreaRaster,
-			Map<CropType, CropUsageData> cropUsageData, Map<CropType, Double> tradeBarriers, RasterSet<IntegerRasterItem> yieldClusters) {
+			Map<CropType, CropUsageData> cropUsageData, Map<CropType, Double> tradeBarriers, RasterSet<IntegerRasterItem> yieldClusters,
+			Map<CropType, Double> subsidyRates) {
 
 		this.demandManager = demandManager;
 		this.country = country;
 		this.tradeBarriers = tradeBarriers;
 		this.yieldClusters = yieldClusters;
+		this.subsidyRates = subsidyRates;
 
 		GamsRasterOutput initialData = new GamsRasterOutput(cropAreaRaster, cropUsageData);
 		previousGamsRasterOutput = initialData;
@@ -266,7 +269,7 @@ public class CountryAgent {
 		Map<CropType, Double> minCerealFract = getMinCerealFraction(currentTimestep);
 
 		GamsCountryInput countryLevelInputs = new GamsCountryInput(country, currentProjectedDemand, currentCountryPrices, importConstraints, 
-				previousGamsRasterOutput.getCropUsageData(), minCerealFract, animalFeedFromOtherSources);	
+				previousGamsRasterOutput.getCropUsageData(), minCerealFract, animalFeedFromOtherSources, subsidyRates);	
 		GamsRasterInput input = new GamsRasterInput(currentTimestep, countryYieldSurfaces, previousGamsRasterOutput.getLandUses(), irrigData, countryLevelInputs);
 
 		return input;
diff --git a/src/ac/ed/lurg/country/SubsidyRateManager.java b/src/ac/ed/lurg/country/SubsidyRateManager.java
new file mode 100644
index 00000000..2b30a346
--- /dev/null
+++ b/src/ac/ed/lurg/country/SubsidyRateManager.java
@@ -0,0 +1,66 @@
+package ac.ed.lurg.country;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ac.ed.lurg.ModelConfig;
+import ac.ed.lurg.types.CropType;
+import ac.ed.lurg.utils.LogWriter;
+import ac.ed.lurg.utils.StringTabularReader;
+
+public class SubsidyRateManager {
+	protected CompositeCountryManager compositeCountryManager;
+	private StringTabularReader tabularReader;
+
+	public SubsidyRateManager(CompositeCountryManager compositeCountryManager) {
+		this.compositeCountryManager = compositeCountryManager;
+		tabularReader = new StringTabularReader(",", new String[]{"country", "crop", "rate"});
+		tabularReader.read(ModelConfig.SUBSIDY_RATE_FILE);
+	}
+
+	public Map<CropType, Double> getSubsidyRates(CompositeCountry cc) {
+
+		Map<CropType, Double> subsidyRates = new HashMap<CropType, Double>();
+		for (SingleCountry c : compositeCountryManager.getAllForCompositeCountry(cc)) {
+			Map<CropType, Double> singleCMap = getSubsidyRates(c);
+
+			for (CropType crop : CropType.getAllItems()) {
+				Double newRateD = singleCMap.get(crop);
+				Double prevRateD = subsidyRates.get(crop);
+
+				// just takes max which isn't right. Should instead weight by production or something else
+				double newRate = Math.max(getDoubleOrZero(newRateD), getDoubleOrZero(prevRateD));
+				subsidyRates.put(crop, newRate);
+			}
+		}
+		return subsidyRates;
+	}
+
+	private double getDoubleOrZero(Double d) {
+		return (d==null ? 0.0 : d.doubleValue());
+	}
+
+	private Map<CropType, Double> getSubsidyRates(SingleCountry c) {
+		Map<CropType, Double> rates = new HashMap<CropType, Double>();
+
+		for (CropType crop : CropType.getAllItems()) {
+			Map<String, String> queryMap = new HashMap<String, String>();
+			queryMap.put("country", c.getCountryName());
+			queryMap.put("crop", crop.getGamsName());
+			try {
+				double subsidy = 0 ;
+				List<Map<String, String>> rows = tabularReader.query(queryMap);
+				if (rows.size() == 1) {
+					String subsidyS = rows.get(0).get("rate");
+					subsidy = Double.valueOf(subsidyS);
+				}
+				rates.put(crop, subsidy);
+			}
+			catch (Exception e) {
+					LogWriter.println("Problem finding cereal demand: " + crop.getFaoName() + ", " + c.getCountryName());
+			}
+		}
+		return rates;
+	}
+}
diff --git a/src/ac/ed/lurg/country/gams/GamsCountryInput.java b/src/ac/ed/lurg/country/gams/GamsCountryInput.java
index 9e244d8c..f53bfa14 100644
--- a/src/ac/ed/lurg/country/gams/GamsCountryInput.java
+++ b/src/ac/ed/lurg/country/gams/GamsCountryInput.java
@@ -20,10 +20,11 @@ public class GamsCountryInput {
 	private Map<CropType, CropUsageData> previousCropUsageData;
 	private Map<CropType, Double> minCerealFraction;
 	private double animalFeedFromOtherSources;
+	private Map<CropType, Double> subsidyRates;
 
 	public GamsCountryInput(CompositeCountry country, Map<CommodityType, Double> projectedDemand, Map<CropType, CountryPrice> countryPrices, 
 			Map<CropType, TradeOrProductionConstraint> importConstraints, Map<CropType, CropUsageData> previousCropUsageData, Map<CropType, Double> minCerealFraction,
-			double animalFeedFromOtherSources) {
+			double animalFeedFromOtherSources, Map<CropType, Double> subsidyRates) {
 		super();
 		this.country = country;
 		this.projectedDemand = projectedDemand;
@@ -32,6 +33,7 @@ public class GamsCountryInput {
 		this.previousCropUsageData = previousCropUsageData;
 		this.minCerealFraction = minCerealFraction;
 		this.animalFeedFromOtherSources = animalFeedFromOtherSources;
+		this.subsidyRates = subsidyRates;
 	}
 		
 	public CompositeCountry getCountry() { 
@@ -83,4 +85,8 @@ public class GamsCountryInput {
 	public double getAnimalFeedFromOtherSources() {
 		return animalFeedFromOtherSources;
 	}
+	
+	public Map<CropType, Double> getSubsidyRates() {
+		return subsidyRates;
+	}
 }
\ No newline at end of file
diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
index 64655fc4..72661ad2 100644
--- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
@@ -232,6 +232,14 @@ public class GamsLocationOptimiser {
 			}
 		}
 
+		if (DEBUG) LogWriter.println("\nCrop, subsidy rate");
+		GAMSParameter subsideRateP = inDB.addParameter("subsidyRate", 1);
+		for (CropType crop : CropType.getNonMeatTypes()) {
+			double subsidyRate = countryInput.getSubsidyRates().get(crop);
+			if (DEBUG) LogWriter.println(String.format("%15s,\t %.4f", crop.getGamsName(), subsidyRate));
+			setGamsParamValue(subsideRateP.addRecord(crop.getGamsName()), subsidyRate, 4);
+		}
+		
 		if (DEBUG) LogWriter.println("\nImport-export, min trade/prod, max trade/prod, global import price, global export price, imports,   exports, ruminantFeed, monogastricFeed, seedAndWasteRate");
 		
 		GAMSParameter minTradeOrProdP = null;
diff --git a/src/ac/ed/lurg/demand/DemandCurve.java b/src/ac/ed/lurg/demand/DemandCurve.java
index 7593ff9a..aa1d5ee6 100644
--- a/src/ac/ed/lurg/demand/DemandCurve.java
+++ b/src/ac/ed/lurg/demand/DemandCurve.java
@@ -3,7 +3,6 @@ package ac.ed.lurg.demand;
 import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.ModelFitType;
-import ac.ed.lurg.utils.LogWriter;
 
 
 public class DemandCurve {
-- 
GitLab