From 3bfba61d0aa8885d57d22a3d889d93bb66438350 Mon Sep 17 00:00:00 2001 From: Peter Alexander <peter@blackhillock.co.uk> Date: Wed, 16 Sep 2015 08:17:49 +0100 Subject: [PATCH] Have different import and export prices --- src/ac/ed/lurg/ModelConfig.java | 5 +- src/ac/ed/lurg/ModelMain.java | 34 ++---- src/ac/ed/lurg/country/CountryAgent.java | 8 +- src/ac/ed/lurg/country/GlobalPrice.java | 47 ++++++++ .../lurg/country/ImportExportConstraint.java | 19 ++++ .../lurg/country/gams/GamsCountryInput.java | 101 ++++++++++-------- .../country/gams/GamsLocationOptimiser.java | 14 ++- .../lurg/country/gams/GamsLocationTest.java | 2 +- .../ed/lurg/country/gams/GamsRasterTest.java | 2 +- 9 files changed, 148 insertions(+), 84 deletions(-) create mode 100644 src/ac/ed/lurg/country/GlobalPrice.java create mode 100644 src/ac/ed/lurg/country/ImportExportConstraint.java diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index 842f780b..0c575fb8 100644 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -147,8 +147,9 @@ public class ModelConfig { public static final double POPULATION_AGGREG_LIMIT = getDoubleProperty("POPULATION_AGGREG_LIMIT", 40.0); // in millions, smaller countries are aggregated on a regional basis public static final double IRRIG_COST_SCALE_FACTOR = getDoubleProperty("IRRIG_COST_SCALE_FACTOR", 2.0); - public static final double TRANSPORT_LOSSES = getDoubleProperty("TRANSPORT_LOSSES", 0.2); // in international trade - + public static final double TRANSPORT_LOSSES = getDoubleProperty("TRANSPORT_LOSSES", 0.15); // in international trade + public static final double TRADE_BARRIER_FACTOR = getDoubleProperty("TRADE_BARRIER_FACTOR", 1.2); // price factor in international trade, transport cost and real trade barriers + public static final int NUM_CEREAL_CATEGORIES = getIntProperty("NUM_CEREAL_CATEGORIES", 5); public static final int NUM_PASTURE_CATEGORIES = getIntProperty("NUM_PASTURE_CATEGORIES", 1); diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java index a2cf9493..4396f933 100644 --- a/src/ac/ed/lurg/ModelMain.java +++ b/src/ac/ed/lurg/ModelMain.java @@ -15,6 +15,7 @@ import ac.ed.lurg.country.CompositeCountryManager; import ac.ed.lurg.country.CountryAgent; import ac.ed.lurg.country.CountryBoundaryRaster; import ac.ed.lurg.country.CountryBoundaryReader; +import ac.ed.lurg.country.GlobalPrice; import ac.ed.lurg.country.gams.GamsRasterOutput; import ac.ed.lurg.demand.BaseConsumpManager; import ac.ed.lurg.demand.DemandManager; @@ -51,7 +52,7 @@ public class ModelMain { private CompositeCountryManager compositeCountryManager; private RasterHeaderDetails desiredProjection; - private Map<CropType, Double> prevWorldPrices; + private Map<CropType, GlobalPrice> prevWorldPrices; private RasterSet<LandUseItem> prevLandUseRaster; public static void main(String[] args) { @@ -72,11 +73,11 @@ public class ModelMain { countryAgents = createCountryAgents(compositeCountryManager.getAll()); // in first timestep we don't have this info, but ok as constrained to import/export specified amount - prevWorldPrices = new HashMap<CropType, Double>(); + prevWorldPrices = new HashMap<CropType, GlobalPrice>(); for (CropType c : CropType.getImportedTypes()) - prevWorldPrices.put(c, 0.3); + prevWorldPrices.put(c, GlobalPrice.createInitial(0.3)); - prevWorldPrices.put(CropType.STARCHY_ROOTS, 1.4); + prevWorldPrices.put(CropType.STARCHY_ROOTS, GlobalPrice.createInitial(1.2)); } /* run the model */ @@ -123,7 +124,6 @@ public class ModelMain { catch (Exception e) { LogWriter.printlnError("Exception processing " + ca.getCountry() + " will continue with other countries"); LogWriter.print(e); - System.exit(1); } if (result == null) { @@ -151,17 +151,17 @@ public class ModelMain { // Look at trade balance and adjust appropriately for (CropType crop : CropType.getImportedTypes()) { - double prevPrice = prevWorldPrices.get(crop); + GlobalPrice prevPrice = prevWorldPrices.get(crop); if (!totalImportCommodities.containsKey(crop) || !totalExportCommodities.containsKey(crop)) { - LogWriter.printlnError(String.format("Price for %s not updated (still %.3f), as no imports or no exports for it", crop.getGamsName(), prevPrice)); + LogWriter.printlnError(String.format("Price for %s not updated (still %s), as no imports or no exports for it", crop.getGamsName(), prevPrice)); continue; } double imports = totalImportCommodities.get(crop); double exports = totalExportCommodities.get(crop) * (1.0-ModelConfig.TRANSPORT_LOSSES); - double adjustedPrice = updateMarketPrices(prevPrice, imports, exports); - LogWriter.println(String.format("Price for %s updated from %.3f (imports %.0f, exports %.0f) to %.3f ", crop.getGamsName(), prevPrice, imports, exports, adjustedPrice)); + GlobalPrice adjustedPrice = prevPrice.updateMarketPrices(imports, exports); + LogWriter.println(String.format("Price for %s updated from %s (imports amount %.0f, exports amount %.0f) to %s ", crop.getGamsName(), prevPrice, imports, exports, adjustedPrice)); prevWorldPrices.put(crop, adjustedPrice); } @@ -173,22 +173,6 @@ public class ModelMain { prevLandUseRaster = globalLandUseRaster; } - public double updateMarketPrices(double previousPrice, double demand, double supply) { - if (demand > 0 || supply > 0) { - double ratio; - - if (demand > supply) - ratio = (demand-supply)/demand; - else - ratio = (demand-supply)/supply; - - double adjustment = Math.exp(ratio * ModelConfig.MARKET_LAMBA); - return previousPrice * adjustment; - } - else { - return previousPrice; - } - } private BufferedWriter getFileWriter(Timestep timestep, String file, String columnHeadings) throws IOException { if (timestep.isInitialTimestep()) { diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java index be122dbf..bdc07ee2 100644 --- a/src/ac/ed/lurg/country/CountryAgent.java +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -62,7 +62,7 @@ public class CountryAgent { return country; } - public GamsRasterOutput determineProduction(Timestep timestep, YieldRaster countryYieldSurfaces, Map<CropType, Double> worldInputPrices) { + public GamsRasterOutput determineProduction(Timestep timestep, YieldRaster countryYieldSurfaces, Map<CropType, GlobalPrice> worldPrices) { currentTimestep = timestep; this.countryYieldSurfaces = countryYieldSurfaces; @@ -77,7 +77,7 @@ public class CountryAgent { } else { // optimize areas and intensity - GamsRasterInput input = getGamsRasterInput(projectedDemand, worldInputPrices); + GamsRasterInput input = getGamsRasterInput(projectedDemand, worldPrices); GamsRasterOptimiser opti = new GamsRasterOptimiser(input); LogWriter.println("Running " + country.getName() + ", currentTimestep " + currentTimestep); @@ -89,7 +89,7 @@ public class CountryAgent { return null; } - private GamsRasterInput getGamsRasterInput(Map<CommodityType, Double> projectedDemand, Map<CropType, Double> worldInputPrices) { + private GamsRasterInput getGamsRasterInput(Map<CommodityType, Double> projectedDemand, Map<CropType, GlobalPrice> worldPrices) { GamsRasterOutput prevOutput; Map<CropType, Double> cropAdjs; @@ -116,7 +116,7 @@ public class CountryAgent { maxOfProdOrSupply.put(entry.getKey(), cropUsage.getProduction() + Math.max(netImports, 0)); } - GamsCountryInput countryLevelInputs = GamsCountryInput.createInput(country, projectedDemand, worldInputPrices, baseNetImport, maxOfProdOrSupply, cropAdjs, calibrate); + GamsCountryInput countryLevelInputs = GamsCountryInput.createInput(country, projectedDemand, worldPrices, baseNetImport, maxOfProdOrSupply, cropAdjs, calibrate); GamsRasterInput input = new GamsRasterInput(currentTimestep, countryYieldSurfaces, prevOutput.getLandUses(), irrigationCostRaster, countryLevelInputs); return input; diff --git a/src/ac/ed/lurg/country/GlobalPrice.java b/src/ac/ed/lurg/country/GlobalPrice.java new file mode 100644 index 00000000..d0666ce2 --- /dev/null +++ b/src/ac/ed/lurg/country/GlobalPrice.java @@ -0,0 +1,47 @@ +package ac.ed.lurg.country; + +import ac.ed.lurg.ModelConfig; + +public class GlobalPrice { + double importPrice; + double exportPrice; + + public GlobalPrice(double importPrice, double exportPrice) { + this.importPrice = importPrice; + this.exportPrice = exportPrice; + } + + public static GlobalPrice createInitial(double exportPrice) { + return new GlobalPrice(exportPrice * ModelConfig.TRADE_BARRIER_FACTOR / (1-ModelConfig.TRANSPORT_LOSSES), exportPrice); + } + + public double getImportPrice() { + return importPrice; + } + + public double getExportPrice() { + return exportPrice; + } + + public GlobalPrice updateMarketPrices(double demand, double supply) { + if (demand > 0 || supply > 0) { + double ratio; + + if (demand > supply) + ratio = (demand-supply)/demand; + else + ratio = (demand-supply)/supply; + + double adjustment = Math.exp(ratio * ModelConfig.MARKET_LAMBA); + return new GlobalPrice(importPrice * adjustment, exportPrice * adjustment); + } + else { + return this; + } + } + + @Override + public String toString() { + return String.format("import=%.3f export=%.3f", importPrice, exportPrice); + } +} diff --git a/src/ac/ed/lurg/country/ImportExportConstraint.java b/src/ac/ed/lurg/country/ImportExportConstraint.java new file mode 100644 index 00000000..c881d924 --- /dev/null +++ b/src/ac/ed/lurg/country/ImportExportConstraint.java @@ -0,0 +1,19 @@ +package ac.ed.lurg.country; + +public class ImportExportConstraint { + double minNetImport; + double maxNetImport; + + public ImportExportConstraint(double minNetImport, double maxNetImport) { + this.minNetImport = minNetImport; + this.maxNetImport = maxNetImport; + } + + public double getMinNetImport() { + return minNetImport; + } + + public double getMaxNetImport() { + return maxNetImport; + } +} diff --git a/src/ac/ed/lurg/country/gams/GamsCountryInput.java b/src/ac/ed/lurg/country/gams/GamsCountryInput.java index d22f1d5a..eed98aa4 100644 --- a/src/ac/ed/lurg/country/gams/GamsCountryInput.java +++ b/src/ac/ed/lurg/country/gams/GamsCountryInput.java @@ -5,6 +5,8 @@ import java.util.Map; import ac.ed.lurg.ModelConfig; import ac.ed.lurg.country.CompositeCountry; +import ac.ed.lurg.country.GlobalPrice; +import ac.ed.lurg.country.ImportExportConstraint; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; @@ -12,13 +14,10 @@ public class GamsCountryInput { private CompositeCountry country; // not really required but useful for debugging private Map<CommodityType, Double> projectedDemand; - private Map<CropType, Double> worldInputPrices; - private Map<CropType, Double> maxNetImport; - private Map<CropType, Double> minNetImport; + private Map<CropType, GlobalPrice> worldPrices; + private Map<CropType, ImportExportConstraint> importConstraints; private Map<CropType, Double> cropAdjustments; private boolean calibrateToObserved; - - // limits to areas for each location and crop /* private double maxLandUseChange; private double meatEfficiency; @@ -27,52 +26,34 @@ public class GamsCountryInput { private double landChangeEnergy;*/ - private GamsCountryInput(CompositeCountry country, Map<CommodityType, Double> projectedDemand, Map<CropType, Double> worldInputPrices, - Map<CropType, Double> maxNetImport, Map<CropType, Double> minNetImport, Map<CropType, Double> cropAdjustments, boolean calibrateToObserved) { + private GamsCountryInput(CompositeCountry country, Map<CommodityType, Double> projectedDemand, Map<CropType, GlobalPrice> worldPrices, + Map<CropType, ImportExportConstraint> importConstraints, Map<CropType, Double> cropAdjustments, boolean calibrateToObserved) { super(); this.country = country; this.projectedDemand = projectedDemand; - this.worldInputPrices = worldInputPrices; - this.maxNetImport = maxNetImport; - this.minNetImport = minNetImport; + this.worldPrices = worldPrices; + this.importConstraints = importConstraints; this.cropAdjustments = cropAdjustments; this.calibrateToObserved = calibrateToObserved; } public GamsCountryInput(GamsCountryInput gamsInput, Map<CropType, Double> cropAdjs) { - this(gamsInput.country, gamsInput.projectedDemand, gamsInput.worldInputPrices, gamsInput.maxNetImport, gamsInput.minNetImport, cropAdjs, false); + this(gamsInput.country, gamsInput.projectedDemand, gamsInput.worldPrices, gamsInput.importConstraints, cropAdjs, false); } - public static GamsCountryInput createInput(CompositeCountry country, Map<CommodityType, Double> projectedDemand, Map<CropType, Double> worldInputPrices, + public static GamsCountryInput createInput(CompositeCountry country, Map<CommodityType, Double> projectedDemand, Map<CropType, GlobalPrice> worldPrices, Map<CropType, Double> baseNetImport, Map<CropType, Double> maxOfProdOrSupply, Map<CropType, Double> cropAdjustments, boolean calibrateToObserved) { - - Map<CropType, Double> maxNetImport; - Map<CropType, Double> minNetImport; - - if (calibrateToObserved) { // this condition is no required as calibration now done from GasmRasterOptimiser rather than in GAMS script - maxNetImport = baseNetImport; - minNetImport = baseNetImport; - - cropAdjustments = new HashMap<CropType, Double>(); // recreate, but might have been null is this case anyway - for (CropType c : CropType.getNonMeatTypes()) { - cropAdjustments.put(c, 1.0); - } - - } - else { - double allowedImportChange = ModelConfig.MAX_IMPORT_CHANGE; - maxNetImport = new HashMap<CropType, Double>(); - minNetImport = new HashMap<CropType, Double>(); - for (Map.Entry<CropType, Double> entry : baseNetImport.entrySet()) { - CropType c = entry.getKey(); - double change = allowedImportChange * maxOfProdOrSupply.get(c); - maxNetImport.put(c, entry.getValue() + change); - minNetImport.put(c, entry.getValue() - change); - } + double allowedImportChange = ModelConfig.MAX_IMPORT_CHANGE; + Map<CropType, ImportExportConstraint> importConstraints = new HashMap<CropType, ImportExportConstraint>(); + + for (Map.Entry<CropType, Double> entry : baseNetImport.entrySet()) { + CropType c = entry.getKey(); + double change = allowedImportChange * maxOfProdOrSupply.get(c); + importConstraints.put(c, new ImportExportConstraint(entry.getValue() - change, entry.getValue() + change)); } - return new GamsCountryInput(country, projectedDemand, worldInputPrices, maxNetImport, minNetImport, cropAdjustments, calibrateToObserved); + return new GamsCountryInput(country, projectedDemand, worldPrices, importConstraints, cropAdjustments, calibrateToObserved); } @@ -84,16 +65,12 @@ public class GamsCountryInput { return projectedDemand; } - public Map<CropType, Double> getWorldInputPrices() { - return worldInputPrices; + public Map<CropType, GlobalPrice> getWorldPrices() { + return worldPrices; } - - public Map<CropType, Double> getMaxNetImport() { - return maxNetImport; - } - - public Map<CropType, Double> getMinNetImport() { - return minNetImport; + + public Map<CropType, ImportExportConstraint> getImportConstraints() { + return importConstraints; } // public Map<CropType, Double> getMinFeedAmount() { @@ -119,4 +96,36 @@ public class GamsCountryInput { public boolean isCalibrateToObserved() { return calibrateToObserved; } + + public Map<CropType, Double> getMinNetImport() { + Map<CropType, Double> netImport = new HashMap<CropType, Double>(); + for (Map.Entry<CropType, ImportExportConstraint> entry : importConstraints.entrySet()) { + netImport.put(entry.getKey(), entry.getValue().getMinNetImport()); + } + return netImport; + } + + public Map<CropType, Double> getMaxNetImport() { + Map<CropType, Double> netImport = new HashMap<CropType, Double>(); + for (Map.Entry<CropType, ImportExportConstraint> entry : importConstraints.entrySet()) { + netImport.put(entry.getKey(), entry.getValue().getMaxNetImport()); + } + return netImport; + } + + public Map<CropType, Double> getWorldImportPrices() { + Map<CropType, Double> prices = new HashMap<CropType, Double>(); + for (Map.Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) { + prices.put(entry.getKey(), entry.getValue().getImportPrice()); + } + return prices; + } + + public Map<CropType, Double> getWorldExportPrices() { + Map<CropType, Double> prices = new HashMap<CropType, Double>(); + for (Map.Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) { + prices.put(entry.getKey(), entry.getValue().getExportPrice()); + } + return prices; + } } diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index 0c71372c..7c8f7aec 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -7,10 +7,12 @@ import java.util.Map.Entry; import java.util.Vector; import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.country.GlobalPrice; +import ac.ed.lurg.country.ImportExportConstraint; import ac.ed.lurg.landuse.CropUsageData; -import ac.ed.lurg.landuse.LandUseItem; import ac.ed.lurg.landuse.Intensity; import ac.ed.lurg.landuse.IrrigationItem; +import ac.ed.lurg.landuse.LandUseItem; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; import ac.ed.lurg.types.LandCoverType; @@ -167,16 +169,18 @@ public class GamsLocationOptimiser { if (DEBUG) { LogWriter.println("\nImport-export, min net imports, max net imports, import price, export price"); for (CropType crop : CropType.getImportedTypes()) { + ImportExportConstraint iec = countryInput.getImportConstraints().get(crop); + GlobalPrice gp = countryInput.getWorldPrices().get(crop); + LogWriter.println(String.format(" %15s, \t %5.1f, \t %5.1f, \t %5.3f, \t %5.3f", - crop.getGamsName(), countryInput.getMinNetImport().get(crop), countryInput.getMaxNetImport().get(crop), - countryInput.getWorldInputPrices().get(crop), countryInput.getWorldInputPrices().get(crop))); + crop.getGamsName(), iec.getMinNetImport(), iec.getMaxNetImport(), gp.getImportPrice(), gp.getExportPrice())); } } addItemMapParm(inDB.addParameter("minNetImport", 1), countryInput.getMinNetImport(), false); addItemMapParm(inDB.addParameter("maxNetImport", 1), countryInput.getMaxNetImport(), false); - addItemMapParm(inDB.addParameter("worldImportPrices", 1), countryInput.getWorldInputPrices(), false); - addItemMapParm(inDB.addParameter("worldExportPrices", 1), countryInput.getWorldInputPrices(), false); + addItemMapParm(inDB.addParameter("worldImportPrices", 1), countryInput.getWorldImportPrices(), false); + addItemMapParm(inDB.addParameter("worldExportPrices", 1), countryInput.getWorldExportPrices(), false); addScalar(inDB.addParameter("meatEfficency", 0), countryInput.getMeatEfficiency()); if (DEBUG) LogWriter.println("\nMeatEfficiency: " + countryInput.getMeatEfficiency()); diff --git a/src/ac/ed/lurg/country/gams/GamsLocationTest.java b/src/ac/ed/lurg/country/gams/GamsLocationTest.java index aac6c0de..b5867c0d 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationTest.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationTest.java @@ -22,7 +22,7 @@ public class GamsLocationTest { } private void run() { - GamsCountryInput countryLevelInputs = GamsCountryInput.createInput(new CompositeCountry("Test"), getProjectedDemand(), getWorldInputEnergy(), getBaseNetImport(), null, null, true); + GamsCountryInput countryLevelInputs = GamsCountryInput.createInput(new CompositeCountry("Test"), getProjectedDemand(), null, null, null, null, true); GamsLocationInput gamsInput = new GamsLocationInput(new Timestep(0), getYields(), getPreviousArea(), getIrrigationCosts(), countryLevelInputs); GamsLocationOptimiser opti = new GamsLocationOptimiser(gamsInput); diff --git a/src/ac/ed/lurg/country/gams/GamsRasterTest.java b/src/ac/ed/lurg/country/gams/GamsRasterTest.java index 8b1c1b7e..d67e8122 100644 --- a/src/ac/ed/lurg/country/gams/GamsRasterTest.java +++ b/src/ac/ed/lurg/country/gams/GamsRasterTest.java @@ -18,7 +18,7 @@ public class GamsRasterTest extends GamsLocationTest { } private void run() { - GamsCountryInput countryLevelInputs = GamsCountryInput.createInput(new CompositeCountry("Test"), getProjectedDemand(), getWorldInputEnergy(), getBaseNetImport(), null, null, true); + GamsCountryInput countryLevelInputs = GamsCountryInput.createInput(new CompositeCountry("Test"), getProjectedDemand(), null, null, null, null, true); GamsRasterInput input = new GamsRasterInput(new Timestep(0), getYieldRaster(), getPreviousAreaRaster(), getIrrigationCost(), countryLevelInputs); GamsRasterOptimiser opti = new GamsRasterOptimiser(input); -- GitLab