Skip to content
Snippets Groups Projects

Merging changes from master

Closed s1713176 requested to merge master into subnational_inequality
3 files
+ 10
10
Compare changes
  • Side-by-side
  • Inline
Files
3
package ac.ed.lurg.demand;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import ac.ed.lurg.ModelConfig;
import ac.ed.lurg.country.CountryPrice;
import ac.ed.lurg.country.SingleCountry;
import ac.ed.lurg.country.gams.*;
import ac.ed.lurg.types.CommodityType;
import ac.ed.lurg.types.CropType;
import ac.ed.lurg.types.WoodCommodityType;
import ac.ed.lurg.types.WoodType;
import ac.ed.lurg.utils.FileWriterHelper;
import ac.ed.lurg.utils.LogWriter;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class ElasticDemandManager extends AbstractSSPDemandManager {
private static final long serialVersionUID = -8605798315853420916L;
private Map<SingleCountry, Map<CommodityType, Double>> baseCpcCache;
private Map<SingleCountry, Map<WoodCommodityType, Double>> baseWoodCpcCache;
private final Map<SingleCountry, Map<CommodityType, Double>> baseCpcCache;
private final Map<SingleCountry, Map<WoodCommodityType, Double>> baseWoodCpcCache;
private transient Map<SingleCountry, GamsDemandOutput> previousGamsDemands;
private transient Map<SingleCountry, GamsWoodDemandOutput> previousGamsWoodDemands;
private transient BufferedWriter outputFile;
private PriceMarkUps priceMarkups;
private transient Object lock;
private final PriceMarkUps priceMarkups;
/** Constructor used in calibration only */
public ElasticDemandManager(String ssp_scenario, BaseConsumpManager baseConsumpManager, CalorieManager calorieManager) {
@@ -44,6 +47,8 @@ public class ElasticDemandManager extends AbstractSSPDemandManager {
} catch (IOException e) {
LogWriter.print(e);
}
lock = new Object();
}
public void setup(String ssp_scenario, BaseConsumpManager baseConsumpManager, CalorieManager calorieManager) {
@@ -52,11 +57,11 @@ public class ElasticDemandManager extends AbstractSSPDemandManager {
}
@Override
Map<CommodityType, Double> getFoodDemandMap(SingleCountry c, int year, double gdpPc, double population,
Map<CommodityType, Double> producerPrices, boolean outputGamsDemand) {
Map<CommodityType, Double> consumerPrices = new HashMap<CommodityType, Double>();
Map<CropType, Double> getFoodDemandMap(SingleCountry c, int year, double gdpPc, double population,
Map<CropType, CountryPrice> countryPrices, boolean outputGamsDemand) {
Map<CommodityType, Double> kcalPerT = calorieManager.get(c);
Map<CommodityType, Double> producerPrices = getCommodityCaloriePrices(c, countryPrices);
Map<CommodityType, Double> consumerPrices = new HashMap<>();
// Cap income to avoid extrapolating beyond the fitted data
gdpPc = Math.min(gdpPc, ModelConfig.DEMAND_INCOME_CAP);
@@ -68,98 +73,120 @@ public class ElasticDemandManager extends AbstractSSPDemandManager {
// convert from producer to consumer prices
for (CommodityType commodity : CommodityType.getAllFoodItems()) {
double producerPricePlum = producerPrices.get(commodity);
// price per person per year assuming 2000 kcal a day (in $ per year)
// $1000 * $1000/t commodity * (1 / kcalPerT) * 2000 kcal per day * 365 days
double producerPrice = 1000 * producerPricePlum * (1 / kcalPerT.get(commodity)) * 2000 * 365;
double consumerPrice = priceMarkups.markedupPrice(c, commodity, producerPrice);
double consumerPrice = priceMarkups.markedupPrice(c, commodity, producerPrices.get(commodity));
consumerPrices.put(commodity, consumerPrice);
}
GamsDemandInput inputData = new GamsDemandInput(c, year, gdpPc, consumerPrices,kcalPerT, previousGamsDemands.get(c));
GamsDemandInput inputData = new GamsDemandInput(c, year, gdpPc, consumerPrices, previousGamsDemands.get(c));
// Do the projection
GamsDemandOptimiser gamsDemandOptimiser = new GamsDemandOptimiser(inputData);
GamsDemandOutput gamsOutput = gamsDemandOptimiser.getDemandPc();
previousGamsDemands.put(c, gamsOutput);
Map<CommodityType, Double> foodPc = gamsOutput.getPlumDemands();
Map<CommodityType, Double> foodDemands = new HashMap<CommodityType, Double>();
Map<CommodityType, Double> modelledDemands = gamsOutput.getTotalDemands(); // kcal per capita per day
Map<CommodityType, Double> rebasedDemands = new HashMap<>(); // kcal per capita per day
Map<CommodityType, Double> baseExpCpcMap = baseCpcCache.get(c);
if (baseExpCpcMap == null) { // we should only do this in the first year
baseCpcCache.put(c, foodPc);
baseExpCpcMap = foodPc;
if (ModelConfig.IS_CALIBRATION_RUN) { // updated each year during calibration so that initial demand = reported
baseCpcCache.put(c, modelledDemands);
}
for (Map.Entry<CommodityType, Double> entry : foodPc.entrySet()) {
Map<CommodityType, Double> baseExpCpcMap = baseCpcCache.get(c);
for (Map.Entry<CommodityType, Double> entry : modelledDemands.entrySet()) {
CommodityType commodity = entry.getKey();
double minTonIntake = (commodity != CommodityType.NONFOOD) ? commodity.getMinFAOCalorieIntake()/kcalPerT.get(commodity) : 0.0;
double minIntake = (commodity != CommodityType.NONFOOD) ? commodity.getMinFAOCalorieIntake() : 0.0;
double newExpCpc = entry.getValue();
double baseExpCpc = baseExpCpcMap.get(commodity);
double baseCpc = baseConsumpManager.get(c, commodity);
double cpc = rebaseConsumption(baseCpc, baseExpCpc, newExpCpc, year);
double cpcAdj = Math.max(cpc, minTonIntake);
double d = cpcAdj * population;
foodDemands.put(commodity, d);
double cpc = rebaseFoodConsumption(baseCpc, baseExpCpc, newExpCpc, year);
double cpcAdj = Math.max(cpc, minIntake);
rebasedDemands.put(commodity, cpcAdj);
}
double foodSpend = 0;
for (CommodityType commodity : CommodityType.getAllFoodItems()) {
double consumerPrice = consumerPrices.get(commodity);
double rebasedDemand = foodDemands.get(commodity);
double caloriesPerT = kcalPerT.get(commodity);
double rebasedDemand = rebasedDemands.get(commodity) / 2000;
foodSpend += consumerPrice*rebasedDemand/population/365/2000*caloriesPerT;
foodSpend += consumerPrice * rebasedDemand;
}
LogWriter.println("foodSpend " + foodSpend + " budget " + gdpPc*ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND, 3);
LogWriter.println("foodSpend " + foodSpend + " budget " + gdpPc * ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND, 3);
if(foodSpend > gdpPc*ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND) {
if(foodSpend > gdpPc * ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND) {
double overspend= 1/(foodSpend/(gdpPc*ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND));
LogWriter.println("overspend " + overspend, 3);
for (Map.Entry<CommodityType, Double> entry : foodPc.entrySet()) {
for (Map.Entry<CommodityType, Double> entry : rebasedDemands.entrySet()) {
CommodityType commodity = entry.getKey();
double rebasedDemand = foodDemands.get(commodity);
double affordableDemand = rebasedDemand*overspend;
foodDemands.put(commodity, affordableDemand);
double rebasedDemand = rebasedDemands.get(commodity);
double affordableDemand = rebasedDemand * overspend;
rebasedDemands.put(commodity, affordableDemand);
}
}
// Disaggregate commodity demand to crops
Map<CropType, Double> rebasedCropDemands = new HashMap<>();
Map<CropType, Double> modelledCropDemands = new HashMap<>();
Map<CommodityType, Map<CropType, Double>> demandFractions = getFoodDemandFractions(c, countryPrices);
Map<CropType, Double> energyContent = calorieManager.get(c);
for (CommodityType comm : rebasedDemands.keySet()) {
for (CropType crop : comm.getCropTypes()) {
double kcalRebased = rebasedDemands.get(comm) * demandFractions.get(comm).get(crop);
double kcalModelled = modelledDemands.get(comm) * demandFractions.get(comm).get(crop);
double kcalPerT = energyContent.get(crop);
double countryDemandRebased = kcalRebased * 365 * population / kcalPerT;
double countryDemandModelled = kcalModelled * 365 * population / kcalPerT;
rebasedCropDemands.put(crop, countryDemandRebased);
modelledCropDemands.put(crop, countryDemandModelled);
}
}
if (outputGamsDemand) writeGamsDemandOutputs(inputData, gamsOutput, foodDemands, population);
if (outputGamsDemand) writeGamsDemandOutputs(inputData, gamsOutput, rebasedCropDemands, modelledCropDemands, rebasedDemands, population);
return foodDemands;
return rebasedCropDemands;
}
private String getDamsDemandOutputsHeader() {
return "Country,Year,gdpPc,population,status,utility,hungerFactor,commodity,price,subsistence,discretionary,plumNotRebase,plumRebased,rebasedKcal";
}
private void writeGamsDemandOutputs(GamsDemandInput inputData, GamsDemandOutput gamsOutput, Map<CommodityType, Double> foodDemands, double population) {
try {
Map<CommodityType, Double> plumDemands = gamsOutput.getPlumDemands();
String initialData = String.format("%s,%d,%.2f,%.6f,%s,%.6f,%.6f",
inputData.getCountry(), inputData.getYear(),inputData.getGdpPc(), population,
gamsOutput.getStatus(), gamsOutput.getUtility(),gamsOutput.getHungerFactor());
for (CommodityType commodity : CommodityType.values()) {
StringBuffer sbData = new StringBuffer(initialData);
GamsCommodityDemand gamsDemand = gamsOutput.getGamsDemands(commodity);
sbData.append(String.format(",%s,%.4f", commodity.getGamsName(), inputData.getPrices().get(commodity)));
sbData.append(String.format(",%.4f,%.4f", gamsDemand.getSubsistence(), gamsDemand.getDiscretionary()));
sbData.append(String.format(",%.4f,%.4f,%.4f", plumDemands.get(commodity), foodDemands.get(commodity)/population,
foodDemands.get(commodity)/population/365*inputData.getKcalPerT(commodity)));
outputFile.write(sbData.toString());
outputFile.newLine();
private void writeGamsDemandOutputs(GamsDemandInput inputData, GamsDemandOutput gamsOutput, Map<CropType, Double> rebasedCropDemands,
Map<CropType, Double> modelledCropDemands, Map<CommodityType, Double> rebasedKcal, double population) {
synchronized (lock) { // needed if using multithreaded to avoid jumbled output
try {
Map<CommodityType, Double> rebasedDemands = new HashMap<>();
Map<CommodityType, Double> modelledDemands = new HashMap<>();
for (CommodityType comm : CommodityType.getAllFoodItems()) {
for (CropType crop : comm.getCropTypes()) {
rebasedDemands.merge(comm, rebasedCropDemands.get(crop), Double::sum);
modelledDemands.merge(comm, modelledCropDemands.get(crop), Double::sum);
}
}
String initialData = String.format("%s,%d,%.2f,%.6f,%s,%.6f,%.6f",
inputData.getCountry(), inputData.getYear(), inputData.getGdpPc(), population,
gamsOutput.getStatus(), gamsOutput.getUtility(), gamsOutput.getHungerFactor());
for (CommodityType commodity : CommodityType.getAllFoodItems()) {
StringBuilder sbData = new StringBuilder(initialData);
GamsCommodityDemand gamsDemand = gamsOutput.getGamsDemands(commodity);
sbData.append(String.format(",%s,%.4f", commodity.getGamsName(), inputData.getPrices().get(commodity)));
sbData.append(String.format(",%.4f,%.4f", gamsDemand.getSubsistence(), gamsDemand.getDiscretionary()));
sbData.append(String.format(",%.4f,%.4f,%.4f", modelledDemands.get(commodity) / population, rebasedDemands.get(commodity) / population,
rebasedKcal.get(commodity)));
outputFile.write(sbData.toString());
outputFile.newLine();
}
} catch (IOException e) {
LogWriter.print(e);
}
}
catch (IOException e) {
LogWriter.print(e);
}
}
@Override
@@ -189,11 +216,13 @@ public class ElasticDemandManager extends AbstractSSPDemandManager {
Map<WoodCommodityType, Double> woodPc = gamsOutput.getPlumDemands();
Map<WoodCommodityType, Double> woodDemands = new HashMap<>();
Map<WoodCommodityType, Double> baseExpCpcMap;
Map<WoodCommodityType, Double> baseExpCpcMap = baseWoodCpcCache.get(country);
if (baseExpCpcMap == null) { // we should only do this in the first year
if (ModelConfig.IS_CALIBRATION_RUN) { // we should only do this during calibration
baseWoodCpcCache.put(country, woodPc);
baseExpCpcMap = woodPc;
} else {
baseExpCpcMap = baseWoodCpcCache.get(country);
}
for (Map.Entry<WoodCommodityType, Double> entry : woodPc.entrySet()) {
@@ -203,9 +232,7 @@ public class ElasticDemandManager extends AbstractSSPDemandManager {
double newExpCpc = entry.getValue();
double baseExpCpc = baseExpCpcMap.get(commodity);
double baseCpc = woodDemandManager.getBaseDemand(country, commodity);
// setting year to BASE_YEAR to avoid convergence. TODO
double cpc = rebaseConsumption(baseCpc, baseExpCpc, newExpCpc, ModelConfig.BASE_YEAR);
cpc = Math.max(cpc, 0); // could be negative due to rebasing so need to max 0
double cpc = rebaseWoodConsumption(baseCpc, baseExpCpc, newExpCpc);
// Rebased consumption
double d = cpc * population;
@@ -223,4 +250,5 @@ public class ElasticDemandManager extends AbstractSSPDemandManager {
return woodTypeDemands;
}
}
\ No newline at end of file
Loading