diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index a1b83bcb394f2230c1840859ad84846ab4402ba9..9bb90990daecbeca2be39f31f50625b9f54f377b 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -57,10 +57,8 @@ PARAMETER conversionCost(land_cover_before, land_cover_after, location) cost of converting from one land cover to another - $1000 per ha; PARAMETER woodYieldMax(location); PARAMETER woodYieldParam(location); - PARAMETER maxRotationIntensity(wood_type); PARAMETER forestBaseCost(managed_forest); PARAMETER previousRotationIntensity(location); - PARAMETER minRotationIntensity(location); SCALAR meatEfficency efficiency of converting feed and pasture into animal products; SCALAR fertiliserUnitCost fert cost at max fert rate; @@ -85,8 +83,8 @@ $load previousCropArea, previousFertIntensity, previousIrrigIntensity, previousO $load yieldNone, yieldFertOnly, yieldIrrigOnly, yieldBoth, yieldShock $load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxNetImport, minNetImport, unhandledCropRate, setAsideRate, maxGrossLccRate, subsidyRate $load meatEfficency, otherICost, irrigCost, irrigMaxRate, irrigConstraint, fertiliserUnitCost, domesticPriceMarkup, minDemandFraction, seedAndWasteRate -$load previousLandCoverArea, maxCroplandArea, carbonCreditRate, conversionCost, woodYieldMax, woodYieldParam, maxRotationIntensity, forestBaseCost, tradeAdjustmentCostRate -$load forestManagementCost, carbonForestMaxProportion, maxFertChange, maxIrrigChange, previousRotationIntensity, minRotationIntensity +$load previousLandCoverArea, maxCroplandArea, carbonCreditRate, conversionCost, woodYieldMax, woodYieldParam, forestBaseCost, tradeAdjustmentCostRate +$load forestManagementCost, carbonForestMaxProportion, maxFertChange, maxIrrigChange, previousRotationIntensity $gdxin SCALAR delta "use to smooth power function see 7.5 www.gams.com dd docs solversconopt.pdf" / 0.00000000001 /; @@ -116,7 +114,7 @@ $gdxin fruitveg 4.0 sugar 3.0 energyCrops 0.3 - pasture 0.38 / ; + pasture 0.42 / ; PARAMETER baseCost(crop) base cost per hectare / wheat 0.10 @@ -304,7 +302,7 @@ $gdxin * Upper and lower bounds on rotation intensity rotationIntensity.UP(location) = 1 / 10; - rotationIntensity.LO(location) = minRotationIntensity(location); + rotationIntensity.LO(location) = 1 / 160; WOOD_SUPPLY_CALC(location) .. totalWoodSupply(location) =E= woodYieldRota(location) * rotationIntensity(location) * landCoverArea('timberForest', location); diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java index 2a1436f723978af33957bbce95f8df734a5aafda..098483d32f5b391047a630966b64deaca081aa5c 100644 --- a/src/ac/ed/lurg/InternationalMarket.java +++ b/src/ac/ed/lurg/InternationalMarket.java @@ -101,12 +101,13 @@ public class InternationalMarket { Map<CropType, Double> initialStockLevels = getInitialStockLevels(); for (Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) { Double initialStock = initialStockLevels.get(entry.getKey()); - if (initialStock != null) - entry.getValue().resetStock(initialStock); + if (initialStock != null) { + entry.getValue().setStockToTargetLevel(); + } } for (Map.Entry<WoodType, GlobalPrice> entry : woodPrices.entrySet()) { - entry.getValue().resetStock(ModelConfig.INIT_WOOD_STOCK); + entry.getValue().setStockToTargetLevel(); } carbonPrice.resetStock(0.0); diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index 43dc2fe1e970aa356fbc030bd338c66e7ec1ab45..a8cb53ac5d02b6c071a74f1a8252550f94540bf7 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -427,7 +427,7 @@ public class ModelConfig { // Import export limits public static final double ANNUAL_MAX_IMPORT_CHANGE = getDoubleProperty("ANNUAL_MAX_IMPORT_CHANGE", 0.5); // Loose constraint as we have TRADE_ADJUSTMENT_COST_RATE public static final double MAX_IMPORT_CHANGE = getDoubleProperty("MAX_IMPORT_CHANGE", ANNUAL_MAX_IMPORT_CHANGE*TIMESTEP_SIZE); - public static final double TRADE_ADJUSTMENT_COST_RATE = getDoubleProperty("TRADE_ADJUSTMENT_COST_RATE", 0.01); + public static final double TRADE_ADJUSTMENT_COST_RATE = getDoubleProperty("TRADE_ADJUSTMENT_COST_RATE", 0.004); // Cost of changing imports/exports // Price caps public static final String PRICE_CAP_FILE = DATA_DIR + File.separator + "price_caps.csv"; @@ -487,8 +487,8 @@ public class ModelConfig { public static final boolean RESET_ENERGYCROP_PRICE = getBooleanProperty("RESET_ENERGYCROP_PRICE", false); // Resets price after calibration to avoid problems due to low initial demand // public static final double BIOENERGY_HEATING_VALUE_GJ_PER_T = getDoubleProperty("BIOENERGY_HEATING_VALUE_GJ_PER_T", 17.5); // GJ per t DM - public static final double MARKET_LAMBDA = getDoubleProperty("MARKET_LAMBDA", 0.6); // controls international market price adjustment rate - public static final double MARKET_DELTA = getDoubleProperty("MARKET_DELTA", 0.002); // price adjustment sensitivity to stock use ratio imbalance + public static final double MARKET_LAMBDA = getDoubleProperty("MARKET_LAMBDA", 1.6); // controls international market price adjustment rate + public static final double MARKET_DELTA = getDoubleProperty("MARKET_DELTA", 0.01); // price adjustment sensitivity to stock use ratio imbalance public static final double DEFAULT_STOCK_USE_RATIO = getDoubleProperty("DEFAULT_STOCK_USE_RATIO", 0.3); public static final String PRICE_UPDATE_METHOD = getProperty("PRICE_UPDATE_METHOD", "AdaptiveLambda"); // Options: AdaptiveLambda, MarketImbalance, StockChange @@ -503,12 +503,12 @@ public class ModelConfig { public static final double UNHANDLED_CROP_RATE = getDoubleProperty("UNHANDLED_CROP_RATE", 0.01323); // mostly forage crops and fibers public static final double SETASIDE_RATE = getDoubleProperty("SETASIDE_RATE", 0.08298); // includes aside, fallow and failed cropland areas - public static final double OTHER_INTENSITY_COST = getDoubleProperty("OTHER_INTENSITY_COST", 1.0); + public static final double OTHER_INTENSITY_COST = getDoubleProperty("OTHER_INTENSITY_COST", 0.8); public static final double OTHER_INTENSITY_PARAM = getDoubleProperty("OTHER_INTENSITY_PARAM", 3.22); public static final double IRRIG_COST_SCALE_FACTOR = getDoubleProperty("IRRIG_COST_SCALE_FACTOR", 0.00035); public static final double IRRIG_COST_MULTIPLIER = getDoubleProperty("IRRIG_COST_MULTIPLIER", 1.0); - public static final double FERTILISER_COST_PER_T = getDoubleProperty("FERTILISER_COST_PER_T", 0.533); // $1000 per tonne. Urea $245/t (2019), 46% nitrogen + public static final double FERTILISER_COST_PER_T = getDoubleProperty("FERTILISER_COST_PER_T", 0.533); // $1000 per tonne. =0.245/0.46, Urea $245/t (2019), 46% nitrogen public static final double FERTILISER_MAX_COST = ModelConfig.getAdjParam("FERTILISER_COST_PER_T") * MAX_FERT_AMOUNT/1000; public static final double DOMESTIC_PRICE_MARKUP = getDoubleProperty("DOMESTIC_PRICE_MARKUP", 1.0); @@ -576,13 +576,13 @@ public class ModelConfig { public static final boolean IS_FORESTRY_ON = getBooleanProperty("IS_FORESTRY_ON", false); public static final String WOOD_DEMAND_FILENAME = getProperty("WOOD_DEMAND_FILENAME", "wood_base_demand.csv"); public static final String WOOD_DEMAND_FILE = getProperty("WOOD_DEMAND_FILE", DATA_DIR + File.separator + WOOD_DEMAND_FILENAME); - public static final double INIT_WOOD_STOCK = getDoubleProperty("INIT_WOOD_STOCK", 600.0); // million m3 + public static final double INIT_WOOD_STOCK = getDoubleProperty("INIT_WOOD_STOCK", 1200.0); // million m3 public static final double WOOD_BIOMASS_CONVERSION_FACTOR = getDoubleProperty("WOOD_BIOMASS_CONVERSION_FACTOR", 0.3); // m3 to tC-eq p.16 [https://doi.org/10.5194/gmd-13-5425-2020] public static final double FOREST_MANAGEMENT_COST = IS_FORESTRY_ON ? getDoubleProperty("FOREST_MANAGEMENT_COST", 2.0) : 0.0; // establishment, management etc. $1000/ha public static final double FOREST_BASE_COST = getDoubleProperty("FOREST_BASE_COST", 0.12); // $1000/ha public static final double WOOD_TRADE_BARRIER = getDoubleProperty("WOOD_TRADE_BARRIER", 0.1); //$1000/m3 - public static final double INIT_ROUNDWOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_ROUNDWOOD_PRICE", 0.1) : 0.0; // $1000/m3 - public static final double INIT_FUELWOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_FUELWOOD_PRICE", 0.07) : 0.0; // $1000/m3 + public static final double INIT_ROUNDWOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_ROUNDWOOD_PRICE", 0.06) : 0.0; // $1000/m3 + public static final double INIT_FUELWOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_FUELWOOD_PRICE", 0.04) : 0.0; // $1000/m3 public static final int CARBON_WOOD_MAX_TIME = getIntProperty("CARBON_WOOD_MAX_TIME", 160); // upper data limit, years // Carbon diff --git a/src/ac/ed/lurg/country/GlobalPrice.java b/src/ac/ed/lurg/country/GlobalPrice.java index 285ae0c4eb18e10d88075336649920630bc2b2d3..614ba70545a3ada03a99668c23184e6640858c26 100644 --- a/src/ac/ed/lurg/country/GlobalPrice.java +++ b/src/ac/ed/lurg/country/GlobalPrice.java @@ -99,11 +99,11 @@ public class GlobalPrice implements Serializable { LogWriter.println(String.format(" updatedStock %.2f, previous %.2f (last timestep %.2f), stockChange %.2f, oldDiff %.2f", updatedStock, stockLevel, stockLevel-oldDiff, stockChange, oldDiff), 2); double adjustment = 1; - double marketLambda = ModelConfig.getAdjParam( "MARKET_LAMBDA"); + double marketLambda = ModelConfig.getAdjParam("MARKET_LAMBDA"); if (!ModelConfig.MARKET_ADJ_PRICE || (ModelConfig.IS_CALIBRATION_RUN && thisTimeStep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION) - || thisTimeStep.getTimestep() == -1) { + || (ModelConfig.IS_CALIBRATION_RUN && thisTimeStep.isInitialTimestep())) { adjustment = 1; } else if (ModelConfig.PRICE_UPDATE_METHOD.equals("AdaptiveLambda")) { @@ -114,8 +114,15 @@ public class GlobalPrice implements Serializable { // Prices increase as a proportion of stockLevel and decrease as a proportion of production double denominator = stockChange < 0 ? stockLevel : production; - adjustment = 1 - lambda * stockChange / denominator - ModelConfig.MARKET_DELTA * (updatedStock - targetStock) / targetStock; - + adjustment = 1 - lambda * stockChange / denominator; + + // Nudge prices when stocks too low (below target level) or too high (50% above target) + double marketDelta = ModelConfig.getAdjParam("MARKET_DELTA"); + if (updatedStock < targetStock & stockChange < 0) { + adjustment *= 1 + marketDelta; + } else if (updatedStock > targetStock * 1.5 & stockChange > 0) { + adjustment *= 1 - marketDelta; + } } else if (ModelConfig.PRICE_UPDATE_METHOD.equals("MarketImbalance")) { @@ -183,6 +190,10 @@ public class GlobalPrice implements Serializable { this.stockLevel = stock; } + public void setStockToTargetLevel() { + stockLevel = production * stockUseRatio; + } + public double getTradeChangeUp(double maxOfProdOrSupply) { if (ModelConfig.DEBUG_LIMIT_COUNTRIES) { return maxOfProdOrSupply * ModelConfig.MAX_IMPORT_CHANGE; diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index 6edc572fdf588a1cf717ca97e8b087755f839db5..74ac942d4ef83baf780d35b8a3e3ec6d4eb249e7 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -201,8 +201,6 @@ public class GamsLocationOptimiser { setGamsParamValue(maxCroplandAreaP.addRecord(locString), landUseItem.getMaxCroplandArea(), 6); setGamsParamValue(prevRotaIntP.addRecord(locString), landUseItem.getForestRotationIntensity(), 6); - double minRotaIntensity = ModelConfig.IS_CALIBRATION_RUN ? 1.0 / 160 : 1 / ((1 / landUseItem.getForestRotationIntensity()) + 1); - setGamsParamValue(minRotaIntP.addRecord(locString), minRotaIntensity, 6); } if (DEBUG) LogWriter.println(String.format(" Total agricultural %.1f,\t suitable %.1f", totalAgriLand, totalSuitable)); @@ -402,8 +400,6 @@ public class GamsLocationOptimiser { // Prices setGamsParamValue(importPriceP.addRecord(woodType.getName()), woodPrices.get(woodType).getImportPrice(), 5); setGamsParamValue(exportPriceP.addRecord(woodType.getName()), woodPrices.get(woodType).getExportPrice(), 5); - // Rotation limits - setGamsParamValue(maxRotaIntensityP.addRecord(woodType.getName()), 1.0 / woodType.getMinRotation(), 6); } addScalar(inDB, "forestManagementCost", ModelConfig.getAdjParam("FOREST_MANAGEMENT_COST"), -1); diff --git a/src/ac/ed/lurg/types/WoodType.java b/src/ac/ed/lurg/types/WoodType.java index 0ae02e8f8d5f7311ce384f0691c0d3da1d8607d7..8c6d9ae61927348f0b2566589e89c7fd447d1108 100644 --- a/src/ac/ed/lurg/types/WoodType.java +++ b/src/ac/ed/lurg/types/WoodType.java @@ -5,16 +5,14 @@ import java.util.Map; import ac.ed.lurg.ModelConfig; public enum WoodType { - IND_ROUNDWOOD("roundwood", 30, ModelConfig.INIT_ROUNDWOOD_PRICE), - FUELWOOD("fuelwood", 10, ModelConfig.INIT_FUELWOOD_PRICE); + IND_ROUNDWOOD("roundwood", ModelConfig.INIT_ROUNDWOOD_PRICE), + FUELWOOD("fuelwood", ModelConfig.INIT_FUELWOOD_PRICE); private String name; - private int minRotation; private double initialPrice; - WoodType(String name, int minRotation, double initialPrice) { + WoodType(String name, double initialPrice) { this.name = name; - this.minRotation = minRotation; this.initialPrice = initialPrice; } @@ -39,10 +37,6 @@ public enum WoodType { return name; } - public int getMinRotation() { - return minRotation; - } - public double getInitialPrice() { return initialPrice; }