diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index ad6ec0e95c4d8522f105bb4db6c13ed416d7c473..9da402d1201d464a84e4644c3b21d8fd6c0b4137 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -66,6 +66,7 @@ SCALAR domesticPriceMarkup factor price increased from cost of production; SCALAR maxLandExpansionRate max rate of country land expansion; + SCALAR woodDemand; SCALAR carbonPrice price of carbon - $1000 per tonne; SCALAR woodPrice price of wood and timber - $1000 per tC-eq; SCALAR forestRotationPeriod; @@ -81,7 +82,7 @@ $load yieldNone, yieldFertOnly, yieldIrrigOnly, yieldBoth, yieldShock $load fertParam, irrigParam, otherIParam, exportPrices, importPrices, maxNetImport, minNetImport, unhandledCropRate, setAsideRate, maxLandExpansionRate, subsidyRate $load meatEfficency, otherICost, irrigCost, irrigMaxRate, irrigConstraint, fertiliserUnitCost, domesticPriceMarkup, minDemandPerCereal, minDemandPerOilcrop, seedAndWasteRate $load previousLandCoverArea, carbonFluxRate, woodYieldLuc, carbonPrice, woodPrice -$load conversionCost, woodYieldRota, infrastructureCost, forestRotationPeriod, woodMaxNetImport, woodMinNetImport, managedForestAreaMaxChangeRate +$load conversionCost, woodYieldRota, infrastructureCost, forestRotationPeriod, woodMaxNetImport, woodMinNetImport, managedForestAreaMaxChangeRate, woodDemand $gdxin SCALAR delta "use to smooth power function see 7.5 www.gams.com/dd/docs/solversconopt.pdf" / 0.00000000001 /; @@ -138,12 +139,14 @@ $gdxin totalConversionCost(location) land cover conversion cost - $1000 per ha woodHarvest(location) wood harvested from timber forest rotation - Mt C-eq woodExported total wood sold - Mt C-eq + woodImported + woodNetSupply carbonFlux(location) total carbon flux - Mt C * A "artificial variable for debugging https://www.gams.com/blog/2017/07/misbehaving-model-infeasible/" total_cost total cost of domestic supply including net imports; POSITIVE VARIABLE cropArea, fertI, irrigI, otherIntensity, ruminantFeed, monogastricFeed, importAmount, exportAmount, - totalFeedDM, landCoverArea, landCoverChange, totalConversionCost, woodHarvest, woodExported; + totalFeedDM, landCoverArea, landCoverChange, totalConversionCost, woodHarvest, woodExported, woodImported; * POSITIVE VARIABLE A; @@ -173,10 +176,12 @@ $gdxin LAND_COVER_CHANGE_CALC(land_cover, location) calc land cover change LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) conservation of land area CONVERSION_COST(location) cost of land cover conversion - MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) - MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) +* MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) +* MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) WOOD_HARVEST_CALC(location) calc wood harvested from timber forest rotation + WOOD_NET_SUPPLY_CALC + WOOD_DEMAND_CONSTRAINT WOOD_EXPORT_CONSTRAINT constraint on maximum wood export WOOD_MIN_TRADE_CONSTRAINT constraint on minimum wood export WOOD_MAX_TRADE_CONSTRAINT @@ -240,22 +245,22 @@ $gdxin CONVERSION_COST(location) .. totalConversionCost(location) =E= sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * conversionCost(land_cover_before, land_cover_after)); -* MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) .. sum(exc_managed_forest, landCoverChange(exc_managed_forest, managed_forest, location)) =L= sum(exc_managed_forest, previousLandCoverArea(exc_managed_forest, location)) * managedForestAreaMaxChangeRate; - -* MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) .. landCoverArea(managed_forest, location) =G= previousLandCoverArea(managed_forest, location) * (1 - managedForestAreaMaxChangeRate); - - MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =L= sum(location, previousLandCoverArea(managed_forest, location)) * (1 + managedForestAreaMaxChangeRate); - - MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =G= sum(location, previousLandCoverArea(managed_forest, location)) * (1 - managedForestAreaMaxChangeRate); +* MANAGED_FOREST_INCREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =L= sum(location, previousLandCoverArea(managed_forest, location)) * (1 + managedForestAreaMaxChangeRate); +* MANAGED_FOREST_DECREASE_CONSTRAINT(managed_forest) .. sum(location, landCoverArea(managed_forest, location)) =G= sum(location, previousLandCoverArea(managed_forest, location)) * (1 - managedForestAreaMaxChangeRate); * Wood from timberForest rotation and new timberForest - WOOD_HARVEST_CALC(location) .. woodHarvest(location) =E= sum(land_cover, landCoverChange(land_cover, 'timberForest', location) * woodYieldRota(land_cover, 'timberForest', location) / forestRotationPeriod); + WOOD_HARVEST_CALC(location) .. woodHarvest(location) =E= sum(land_cover, landCoverChange(land_cover, 'timberForest', location) * woodYieldRota(land_cover, 'timberForest', location) / forestRotationPeriod) + + sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * woodYieldLuc(land_cover_before, land_cover_after, location)); + + WOOD_NET_SUPPLY_CALC .. woodNetSupply =E= sum(location, woodHarvest(location)) - woodExported + woodImported; + + WOOD_DEMAND_CONSTRAINT .. woodNetSupply =G= woodDemand; - WOOD_EXPORT_CONSTRAINT .. woodExported =E= sum(location, woodHarvest(location)); + WOOD_EXPORT_CONSTRAINT .. woodExported =L= sum(location, woodHarvest(location)); - WOOD_MIN_TRADE_CONSTRAINT .. 0 - woodExported =L= woodMaxNetImport; + WOOD_MIN_TRADE_CONSTRAINT .. woodImported - woodExported =L= woodMaxNetImport; - WOOD_MAX_TRADE_CONSTRAINT .. 0 - woodExported =G= woodMinNetImport; + WOOD_MAX_TRADE_CONSTRAINT .. woodImported - woodExported =G= woodMinNetImport; CARBON_FLUX_CALC(location) .. carbonFlux(location) =E= sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * carbonFluxRate(land_cover_before, land_cover_after, location)); @@ -268,7 +273,7 @@ $gdxin SUM(location, totalConversionCost(location)) + - [-woodPrice * woodExported] + + [woodPrice * (woodImported - woodExported)] + SUM(location, carbonFlux(location)) * carbonPrice ); @@ -286,7 +291,6 @@ $gdxin exportAmount.L(all_types) = previousExportAmount(all_types); totalConversionCost.L(location) = sum(land_cover, landCoverChange.L(land_cover, land_cover, location) * conversionCost(land_cover, land_cover)); woodHarvest.L(location) = previousLandCoverArea('timberForest', location) * woodYieldRota('timberForest', 'timberForest', location) / forestRotationPeriod; - woodExported.L = sum(location, woodHarvest.L(location)); * LAND_USE.OptFile = 1; @@ -308,6 +312,8 @@ $gdxin parameter feedCostRate(feed_crop); parameter productionShock(all_types); scalar netCarbonFlux; + scalar netWoodImport; + scalar totalWoodHarvest; * Production quantities based on smaller area (before unhandledCropArea adjustment applied) totalProd(crop) = sum(location, cropArea.l(crop, location) * yield.l(crop, location)); @@ -329,6 +335,8 @@ $gdxin netImportAmount(import_crop) = importAmount.l(import_crop) - exportAmount.l(import_crop); netCarbonFlux = SUM(location, carbonFlux.L(location)); + netWoodImport = woodImported.L - woodExported.L; + totalWoodHarvest = sum(location, woodHarvest.L(location)); Scalar totalCostsLessLU; diff --git a/debug_config.properties b/debug_config.properties index 92d926b6c77a203f249e9d7d14fad51f886ea83c..1432cdd4b78a712a13d35ca2707c5ba6f0039848 100644 --- a/debug_config.properties +++ b/debug_config.properties @@ -10,7 +10,7 @@ TIMESTEP_SIZE=1 END_TIMESTEP=90 IS_CALIBRATION_RUN=true -END_FIRST_STAGE_CALIBRATION=10 +END_FIRST_STAGE_CALIBRATION=2 GENERATE_NEW_YIELD_CLUSTERS=false @@ -20,5 +20,12 @@ DEBUG_LIMIT_COUNTRIES=true DEBUG_COUNTRY_NAME=Brazil GAMS_COUNTRY_TO_SAVE=Brazil -WOOD_PRICE=0.1 -CARBON_PRICE=0 \ No newline at end of file +INIT_WOOD_PRICE=0.2 +INIT_WOOD_STOCK=1000 +INIT_CARBON_PRICE=0.0 +FOREST_ROTATION_PERIOD=30 +INFRASTRUCTURE_EXPANSION_COST=0.0 +MANAGED_FOREST_INCREASE_COST=0.03 +MANAGED_FOREST_DECREASE_COST=0.03 +FOREST_MANAGEMENT_COST=0.05 +FOREST_MAX_CHANGE=0.02 \ No newline at end of file diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java index 2a749832361ab224a8184fb3bfcfe52e9b4e1ff8..bd5735fae189417f01da2323e43dfaad28e017e9 100644 --- a/src/ac/ed/lurg/InternationalMarket.java +++ b/src/ac/ed/lurg/InternationalMarket.java @@ -71,7 +71,7 @@ public class InternationalMarket { return initialStockLevels; } - void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double gen2EcDDemand, double carbonDemand, double timberDemand, Timestep timestep) { + void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double gen2EcDDemand, double carbonDemand, Timestep timestep) { CropToDoubleMap totalImportCommodities = new CropToDoubleMap(); CropToDoubleMap totalExportCommodities = new CropToDoubleMap(); CropToDoubleMap totalProduction = new CropToDoubleMap(); @@ -133,17 +133,25 @@ public class InternationalMarket { carbonPrice = adjustedCPrice; // Update timber price - double totalTimberProduced = 0; + double totalWoodImport = 0; + double totalWoodExport = 0; + double totalWoodProduction = 0; for (AbstractCountryAgent ca : countryAgents) { - totalTimberProduced += ca.getTimberProduction(); + totalWoodProduction += ca.getWoodUsageData().getHarvest(); + double netImport = ca.getWoodUsageData().getNetImport(); + if (netImport >= 0) { + totalWoodImport += netImport; + } else { + totalWoodExport += -netImport; + } } - totalTimberProduced = Math.max(totalTimberProduced, 0.0000001); // avoid division by 0 + totalWoodProduction = Math.max(totalWoodProduction, 0.0000001); // avoid division by 0 GlobalPrice prevTPrice = woodPrice; - LogWriter.println(timestep.getYear() + " Updating timber price"); - GlobalPrice adjustedTPrice = prevTPrice.createWithUpdatedMarketPrices(timberDemand, totalTimberProduced, timestep, totalTimberProduced, false); - LogWriter.println( String.format("Price for timber updated from %s to %s \n", prevTPrice, adjustedTPrice)); + LogWriter.println(timestep.getYear() + " Updating wood price"); + GlobalPrice adjustedTPrice = prevTPrice.createWithUpdatedMarketPrices(totalWoodImport, totalWoodExport, timestep, totalWoodProduction, true); + LogWriter.println( String.format("Price for wood updated from %s to %s \n", prevTPrice, adjustedTPrice)); if (adjustedTPrice.getStockLevel() < 0) - LogWriter.println("Global stocks are below zero timber, " + timestep.getYear()); + LogWriter.println("Global stocks are below zero wood, " + timestep.getYear()); woodPrice = adjustedTPrice; } diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index 3ba6b590328f1bdd7e9fe81fc81d9b9ba3e3ffe9..f1d0c3031e3818a984546c5729f688fa1edfe313 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -437,5 +437,7 @@ public class ModelConfig { public static final double MANAGED_FOREST_INCREASE_COST = getDoubleProperty("MANAGED_FOREST_INCREASE_COST", 0.5 * LAND_CHANGE_COST); // $1000/ha public static final double MANAGED_FOREST_DECREASE_COST = getDoubleProperty("MANAGED_FOREST_DECREASE_COST", 0.3 * LAND_CHANGE_COST); // $1000/ha public static final double INFRASTRUCTURE_EXPANSION_COST = getDoubleProperty("INFRASTRUCTURE_EXPANSION_COST", 0.2); // $1000/ha - public static final double FOREST_MAX_CHANGE = getDoubleProperty("FOREST_MAX_CHANGE", 0.02); // $1000/ha + public static final double FOREST_MAX_CHANGE = getDoubleProperty("FOREST_MAX_CHANGE", 0.02); + public static final double ROUNDWOOD_DEMAND_ELASTICITY = getDoubleProperty("ROUNDWOOD_DEMAND_ELASTICITY", 0.5); + public static final double FUELWOOD_DEMAND_ELASTICITY = getDoubleProperty("FUELDWOOD_DEMAND_ELASTICITY", -0.5); } diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java index 0871123917c4a93dd83df55c43daf474e852b0c7..357c675b64f4841486f5938556f2e25fd37ce19f 100644 --- a/src/ac/ed/lurg/ModelMain.java +++ b/src/ac/ed/lurg/ModelMain.java @@ -147,25 +147,21 @@ public class ModelMain { double carbonDemand = demandManager.getCarbonDemand(ModelConfig.IS_CALIBRATION_RUN ? new Timestep(1) : timestep); double carbonDemandIncrease = (carbonDemand > previousCarbonDemand) ? carbonDemand - previousCarbonDemand: 0; - double previousTimberDemand = (timestep.isInitialTimestep() || ModelConfig.IS_CALIBRATION_RUN ) ? 0: demandManager.getTimberDemand(timestep.getPreviousTimestep()); - double timberDemand = demandManager.getTimberDemand(ModelConfig.IS_CALIBRATION_RUN ? new Timestep(1) : timestep); - double timberDemandIncrease = (timberDemand > previousTimberDemand) ? timberDemand - previousTimberDemand: 0; - CarbonFluxRasterSet currentCarbonFluxData = getCarbonFluxData(timestep); WoodYieldRasterSet currentWoodYieldData = getWoodYieldData(timestep); DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts = new ConversionCostReader().read(); countryAgents.determineProductionForAll(timestep, yieldSurfaces, currentIrrigationData, gen2Increase, currentCarbonFluxData, - currentWoodYieldData, conversionCosts, carbonDemandIncrease, timberDemandIncrease); - internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timberDemand, timestep); + currentWoodYieldData, conversionCosts, carbonDemandIncrease); + internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timestep); // loop for iterations. Could check within a tolerance using internationalMarket.findMaxPriceDiff, not doing so as doesn't find a solution due to inelastic consumption for (int i=0; i < ModelConfig.DEMAND_RECALC_MAX_ITERATIONS; i++) { LogWriter.println("\n++ Re-estimating prices and demand " + i); countryAgents.recalculateDemandForAll(); // recalculate demand from new prices countryAgents.updateNetImportsForAll(); // calculate imports and exports - internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timberDemand, timestep); // calculate prices + internationalMarket.determineInternationalTrade(countryAgents.getAll(), gen2EcDDemand, carbonDemand, timestep); // calculate prices } internationalMarket.applyPriceShocks(timestep); @@ -574,7 +570,7 @@ public class ModelMain { Map<CompositeCountry, Double[]> initWoodAndCarbonProd; if (ModelConfig.IS_CALIBRATION_RUN) { initWoodAndCarbonProd = new HashMap<CompositeCountry, Double[]>(); - Double[] initProdValues = {0.0, 0.0}; + Double[] initProdValues = {0.0, 0.0}; // TODO Read in for (CompositeCountry cc : compositeCountryManager.getAll()) { initWoodAndCarbonProd.put(cc, initProdValues); } diff --git a/src/ac/ed/lurg/country/AbstractCountryAgent.java b/src/ac/ed/lurg/country/AbstractCountryAgent.java index 8aa5d9c87cc0256fe372206449cb3ce96b08bf14..58d7adf9be0182676998634ca4bc135db80d7243 100644 --- a/src/ac/ed/lurg/country/AbstractCountryAgent.java +++ b/src/ac/ed/lurg/country/AbstractCountryAgent.java @@ -7,8 +7,10 @@ import ac.ed.lurg.ModelConfig; import ac.ed.lurg.Timestep; import ac.ed.lurg.demand.AbstractDemandManager; import ac.ed.lurg.landuse.CropUsageData; +import ac.ed.lurg.landuse.WoodUsageData; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.WoodType; import ac.ed.lurg.utils.LogWriter; public abstract class AbstractCountryAgent { @@ -23,6 +25,7 @@ public abstract class AbstractCountryAgent { protected Map<CommodityType, Map<CropType, Double>> baseDemandFact; protected Timestep currentTimestep; protected Map<CommodityType, Map<CropType, Double>> currentMinDemandFract; + protected Map<WoodType, Double> currentWoodDemand; public AbstractCountryAgent(AbstractDemandManager demandManager,CompositeCountry country, Map<CropType, Double> tradeBarriers) { @@ -60,6 +63,7 @@ public abstract class AbstractCountryAgent { private void calculateDemand(boolean outputGamsDemand) { Map<CommodityType, Double> producerPrices = getProducerCommodityPrices(); currentProjectedDemand = demandManager.getDemand(country, currentTimestep.getYear(), producerPrices, outputGamsDemand); + currentWoodDemand = demandManager.getWoodDemandComposite(country, currentTimestep.getYear()); } protected void calculateCountryPricesAndDemand(Map<CropType, GlobalPrice> worldPrices, GlobalPrice carbonPrice, GlobalPrice timberPrice, boolean outputGamsDemand) { @@ -176,6 +180,6 @@ public abstract class AbstractCountryAgent { abstract public double getNetCarbonFlux(); - abstract public double getTimberProduction(); + abstract public WoodUsageData getWoodUsageData(); } \ No newline at end of file diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java index 1289c2206d267c23017a8a7f76df78f2b19a10d4..184e433c2b8db64071c7847fdcd2c507b4f4e764 100644 --- a/src/ac/ed/lurg/country/CountryAgent.java +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -20,6 +20,7 @@ import ac.ed.lurg.landuse.CarbonFluxItem; import ac.ed.lurg.landuse.CropUsageData; import ac.ed.lurg.landuse.IrrigationItem; import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.WoodUsageData; import ac.ed.lurg.landuse.WoodYieldItem; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; @@ -106,7 +107,7 @@ public class CountryAgent extends AbstractCountryAgent { public GamsRasterOutput determineProduction(YieldRaster countryYieldSurfaces, RasterSet<IrrigationItem> irrigData, Map<CropType, GlobalPrice> worldPrices, double globalGen2EcIncrease, RasterSet<CarbonFluxItem> carbonFluxData, RasterSet<WoodYieldItem> woodYieldData, DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts, - double carbonDemandIncrease, GlobalPrice carbonPrice, double timberDemandIncrease, GlobalPrice timberPrice) { + double carbonDemandIncrease, GlobalPrice carbonPrice, GlobalPrice timberPrice) { // project demand calculateCountryPricesAndDemand(worldPrices, carbonPrice, timberPrice, false); @@ -129,7 +130,7 @@ public class CountryAgent extends AbstractCountryAgent { // optimize areas and intensity GamsRasterInput input = getGamsRasterInput(irrigData, countryYieldSurfaces, globalGen2EcIncrease, carbonFluxData, woodYieldData, conversionCosts, - carbonDemandIncrease, timberDemandIncrease); + carbonDemandIncrease); GamsRasterOptimiser opti = new GamsRasterOptimiser(input, yieldClusters); LogWriter.println("Running " + country.getName() + ", currentTimestep " + currentTimestep); @@ -168,7 +169,7 @@ public class CountryAgent extends AbstractCountryAgent { private GamsRasterInput getGamsRasterInput(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces, double gen2EcIncrease, RasterSet<CarbonFluxItem> carbonFluxData, RasterSet<WoodYieldItem> woodYieldData, DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts, - double carbonDemandIncrease, double timberDemandIncrease) { + double carbonDemandIncrease) { double allowedImportChange; if (currentTimestep.isInitialTimestep() || (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION)) { // initialisation time-step @@ -226,30 +227,22 @@ public class CountryAgent extends AbstractCountryAgent { // Timber import/export constraints TradeConstraint timberTradeConstraint; { - double baseTrade = -getTimberProduction(); // net imports negative as all exported - double countryArea = LandUseItem.getTotalLandArea(previousGamsRasterOutput.getLandUses().values()); + double baseTrade; + if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.isInitialTimestep()) { + baseTrade = -1; + } else { + baseTrade = getWoodUsageData().getNetImport(); + } double changeUp = 0.0; double changeDown = 0.0; if (allowedImportChange > 0.0) { - changeDown = Math.min(baseTrade * allowedImportChange, -(timberDemandIncrease * countryArea / 15000 * 2)); - changeUp = baseTrade * allowedImportChange; + changeUp = changeDown = baseTrade * allowedImportChange; } - // Initial timber production for calibration run - if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.isInitialTimestep()) { - double forestedAndNaturalArea = LandUseItem.getTotalLandCover(previousGamsRasterOutput.getLandUses().values(), LandCoverType.NATURAL) + - LandUseItem.getTotalLandCover(previousGamsRasterOutput.getLandUses().values(), LandCoverType.TIMBER_FOREST); - changeDown = -timberDemandIncrease * forestedAndNaturalArea / 4000; - if (ModelConfig.DEBUG_LIMIT_COUNTRIES) {changeDown = -timberDemandIncrease;} // for debugging purposes only - } else { - changeDown = Math.min(baseTrade * allowedImportChange, -(timberDemandIncrease * countryArea / 15000 * 2)); // allow export in case baseTrade is 0 - } - changeUp = baseTrade * allowedImportChange; - timberTradeConstraint = new TradeConstraint(baseTrade + changeDown, baseTrade - changeUp); } GamsCountryInput countryLevelInputs = new GamsCountryInput(country, currentProjectedDemand, currentCountryPrices, importConstraints, - previousGamsRasterOutput.getCropUsageData(), currentMinDemandFract, subsidyRates, currentCarbonPrice, carbonTradeConstraint, currentTimberPrice, timberTradeConstraint); + previousGamsRasterOutput.getCropUsageData(), currentMinDemandFract, subsidyRates, currentCarbonPrice, carbonTradeConstraint, currentWoodDemand, currentTimberPrice, timberTradeConstraint); GamsRasterInput input = new GamsRasterInput(currentTimestep, countryYieldSurfaces, previousGamsRasterOutput.getLandUses(), irrigData, carbonFluxData, woodYieldData, conversionCosts, countryLevelInputs); @@ -343,7 +336,7 @@ public class CountryAgent extends AbstractCountryAgent { return previousGamsRasterOutput.getNetCarbonFlux(); } - public double getTimberProduction() { - return previousGamsRasterOutput.getTimberProduction(); + public WoodUsageData getWoodUsageData() { + return previousGamsRasterOutput.getWoodUsageData(); } } \ No newline at end of file diff --git a/src/ac/ed/lurg/country/CountryAgentManager.java b/src/ac/ed/lurg/country/CountryAgentManager.java index 0111e52c1703957cd421e015357832dd9e0e6f61..56d07768a0a823926876dfa19c0fb1f9dd5744b4 100644 --- a/src/ac/ed/lurg/country/CountryAgentManager.java +++ b/src/ac/ed/lurg/country/CountryAgentManager.java @@ -106,7 +106,7 @@ public class CountryAgentManager { public void determineProductionForAll(Timestep timestep, YieldRaster yieldSurfaces, IrrigationRasterSet currentIrrigationData, double gen2Increase, CarbonFluxRasterSet currentCarbonFluxData, WoodYieldRasterSet currentWoodYieldData, DoubleMap<LandCoverType, LandCoverType, Double> conversionCosts, - double carbonDemandIncrease, double timberDemandIncrease) { + double carbonDemandIncrease) { for (AbstractCountryAgent aca : countryAgents) { aca.setCurrentTimestep(timestep); @@ -122,7 +122,7 @@ public class CountryAgentManager { try { ca.determineProduction(countryYieldSurfaces, irrigData, internationalMarket.getWorldPrices(), gen2Increase, carbonFluxData, woodYieldData, - conversionCosts, carbonDemandIncrease, internationalMarket.getCarbonPrice(), timberDemandIncrease, internationalMarket.getWoodPrice()); + conversionCosts, carbonDemandIncrease, internationalMarket.getCarbonPrice(), internationalMarket.getWoodPrice()); // update global rasters globalLandUseRaster.putAll(ca.getLandUses()); @@ -187,7 +187,7 @@ public class CountryAgentManager { public void serializeTimberAndCarbonProdForAll() { Map<CompositeCountry, Double[]> timberAndCarbonProdMap = new HashMap<CompositeCountry, Double[]>(); for (CountryAgent ca : gamsCountryAgents) { - Double[] timberAndCarbonProd = {ca.getTimberProduction(), ca.getNetCarbonFlux()}; + Double[] timberAndCarbonProd = {ca.getWoodUsageData().getNetImport(), ca.getNetCarbonFlux()}; timberAndCarbonProdMap.put(ca.country, timberAndCarbonProd); } diff --git a/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java index dc5d375319f53186bbc92df4b924cb0738090200..2a14d09916880a40110f963b8048f3cdc944ad0b 100644 --- a/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java +++ b/src/ac/ed/lurg/country/crafty/CraftyCountryAgent.java @@ -9,6 +9,7 @@ import ac.ed.lurg.country.CompositeCountry; import ac.ed.lurg.country.GlobalPrice; import ac.ed.lurg.demand.AbstractDemandManager; import ac.ed.lurg.landuse.CropUsageData; +import ac.ed.lurg.landuse.WoodUsageData; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; @@ -54,8 +55,10 @@ public class CraftyCountryAgent extends AbstractCountryAgent { public double getNetCarbonFlux() { return 0; } - - public double getTimberProduction() { - return 0; + + @Override + public WoodUsageData getWoodUsageData() { + // TODO Auto-generated method stub + return null; } } \ No newline at end of file diff --git a/src/ac/ed/lurg/country/gams/GamsCountryInput.java b/src/ac/ed/lurg/country/gams/GamsCountryInput.java index f8298e5b05467ec984da49cb81d3add0f897bdd0..252b1bbc0d61dbd224a76f1bb43351bc7e788722 100644 --- a/src/ac/ed/lurg/country/gams/GamsCountryInput.java +++ b/src/ac/ed/lurg/country/gams/GamsCountryInput.java @@ -10,6 +10,7 @@ import ac.ed.lurg.country.TradeConstraint; import ac.ed.lurg.landuse.CropUsageData; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.WoodType; public class GamsCountryInput { @@ -22,13 +23,15 @@ public class GamsCountryInput { private Map<CropType, Double> subsidyRates; private CountryPrice carbonPrice; private TradeConstraint carbonTradeConstraint; + private Map<WoodType, Double> woodDemand; private CountryPrice woodPrice; private TradeConstraint timberTradeConstraint; public GamsCountryInput(CompositeCountry country, Map<CommodityType, Double> projectedDemand, Map<CropType, CountryPrice> countryPrices, Map<CropType, TradeConstraint> importConstraints, Map<CropType, CropUsageData> previousCropUsageData, Map<CommodityType, Map<CropType, Double>> minDemandFracts, Map<CropType, Double> subsidyRates, - CountryPrice carbonPrice, TradeConstraint carbonTradeConstraint, CountryPrice woodPrice, TradeConstraint timberTradeConstraint) { + CountryPrice carbonPrice, TradeConstraint carbonTradeConstraint, Map<WoodType, Double> woodDemand, + CountryPrice woodPrice, TradeConstraint timberTradeConstraint) { super(); this.country = country; this.projectedDemand = projectedDemand; @@ -39,6 +42,7 @@ public class GamsCountryInput { this.subsidyRates = subsidyRates; this.carbonPrice = carbonPrice; this.carbonTradeConstraint = carbonTradeConstraint; + this.woodDemand = woodDemand; this.woodPrice = woodPrice; this.timberTradeConstraint = timberTradeConstraint; } @@ -101,6 +105,10 @@ public class GamsCountryInput { return carbonTradeConstraint; } + public Map<WoodType, Double> getWoodDemand() { + return woodDemand; + } + public CountryPrice getWoodPrice() { return woodPrice; } diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index 3a55910ef04609097e6526376d2be5f032fffac6..211385243c1735e39aa1ac2644d46fd5305ba082 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -30,11 +30,13 @@ import ac.ed.lurg.landuse.CropUsageData; import ac.ed.lurg.landuse.Intensity; import ac.ed.lurg.landuse.IrrigationItem; import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.WoodUsageData; import ac.ed.lurg.landuse.WoodYieldItem; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; import ac.ed.lurg.types.LandCoverType; import ac.ed.lurg.types.Parameter; +import ac.ed.lurg.types.WoodType; import ac.ed.lurg.types.YieldType; import ac.ed.lurg.utils.DoubleMap; import ac.ed.lurg.utils.LazyHashMap; @@ -342,6 +344,12 @@ public class GamsLocationOptimiser { addScalar(inDB, "woodMaxNetImport", countryInput.getWoodTradeConstraint().getMaxConstraint(), 5); addScalar(inDB, "woodMinNetImport", countryInput.getWoodTradeConstraint().getMinConstraint(), 5); + // Wood demand + Map<WoodType, Double> woodDemandMap = countryInput.getWoodDemand(); + double totalWoodDemand = woodDemandMap.values().stream().reduce(0.0, Double::sum); + addScalar(inDB, "woodDemand", totalWoodDemand, 5); + + // Carbon fluxes GAMSParameter cFluxRateP = inDB.addParameter("carbonFluxRate", 3); @@ -604,9 +612,11 @@ public class GamsLocationOptimiser { double netCarbonFlux = outDB.getParameter("netCarbonFlux").getFirstRecord().getValue(); // Timber harvest - double totalTimberProduction = outDB.getVariable("woodExported").getFirstRecord().getLevel(); + double totalTimberProduction = outDB.getParameter("totalWoodHarvest").getFirstRecord().getValue(); + double netWoodImport = outDB.getParameter("netWoodImport").getFirstRecord().getValue(); + WoodUsageData woodUsageData = new WoodUsageData(totalTimberProduction, netWoodImport); - GamsLocationOutput results = new GamsLocationOutput(modelStatus, landUses, cropUsageData, landCoverChanges, gamsLandCoverChanges, netCarbonFlux, totalTimberProduction); + GamsLocationOutput results = new GamsLocationOutput(modelStatus, landUses, cropUsageData, landCoverChanges, gamsLandCoverChanges, netCarbonFlux, woodUsageData); return results; } diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOutput.java b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java index f475de6d4e90f446ad11c28bfd85f5f54ab88d56..0876507f0325a3868d6cede31eaae06fb62d3704 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOutput.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java @@ -7,6 +7,7 @@ import com.gams.api.GAMSGlobals.ModelStat; import ac.ed.lurg.landuse.CropUsageData; import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.WoodUsageData; import ac.ed.lurg.types.CropType; import ac.ed.lurg.utils.TripleMap; import ac.ed.lurg.types.LandCoverType; @@ -20,14 +21,14 @@ public class GamsLocationOutput { private TripleMap<Integer, LandCoverType, LandCoverType, Double> landCoverChanges; ArrayList<LandCoverChangeItem> gamsLandCoverChanges; private double netCarbonFlux; - private double timberProduction; + private WoodUsageData woodUsageData; public GamsLocationOutput(ModelStat status, Map<Integer, LandUseItem> landUses, Map<CropType, CropUsageData> cropUsageData, TripleMap<Integer, LandCoverType, LandCoverType, Double> landCoverChange, ArrayList<LandCoverChangeItem> gamsLandCoverChanges, - double netCarbonFlux, double timberProduction) { + double netCarbonFlux, WoodUsageData woodUsageData) { super(); this.status = status; this.landUses = landUses; @@ -35,7 +36,7 @@ public class GamsLocationOutput { this.landCoverChanges = landCoverChange; this.gamsLandCoverChanges = gamsLandCoverChanges; this.netCarbonFlux = netCarbonFlux; - this.timberProduction = timberProduction; + this.woodUsageData = woodUsageData; } public ModelStat getStatus() { @@ -60,8 +61,8 @@ public class GamsLocationOutput { public double getNetCarbonFlux() { return netCarbonFlux; } - - public double getTimberProduction() { - return timberProduction; + + public WoodUsageData getWoodUsageData() { + return woodUsageData; } } diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java index 049d10a1b9be86ed34d2ca5d898d82c7b0eae730..a724510f0779a3e155ee49f4d72e4aa0f6a94beb 100644 --- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java @@ -59,7 +59,7 @@ public class GamsRasterOptimiser { } return new GamsRasterOutput(gamsOutput.getStatus(), newIntensityRaster, gamsOutput.getCommoditiesData(), gamsOutput.getGamsLandCoverChanges(), - baseTimberYield, gamsOutput.getNetCarbonFlux(), gamsOutput.getTimberProduction()); + baseTimberYield, gamsOutput.getNetCarbonFlux(), gamsOutput.getWoodUsageData()); } private RasterSet<LandUseItem> createWithSameLandCovers(RasterSet<LandUseItem> toCopy) { diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOutput.java b/src/ac/ed/lurg/country/gams/GamsRasterOutput.java index 24af77bc2c05a13768384d4da499a654e7520a4b..cade1516dc2e41e9fab56c3634f3b88145617bbf 100644 --- a/src/ac/ed/lurg/country/gams/GamsRasterOutput.java +++ b/src/ac/ed/lurg/country/gams/GamsRasterOutput.java @@ -8,6 +8,7 @@ import com.gams.api.GAMSGlobals.ModelStat; import ac.ed.lurg.country.LandCoverChangeItem; import ac.ed.lurg.landuse.CropUsageData; import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.WoodUsageData; import ac.ed.lurg.types.CropType; import ac.sac.raster.RasterSet; @@ -18,8 +19,8 @@ public class GamsRasterOutput { private Map<CropType, CropUsageData> cropUsageData; private ArrayList<LandCoverChangeItem> gamsLandCoverChanges; private Map<Integer, Double> baseTimberYield; - double netCarbonFlux; - double woodProduction; + private double netCarbonFlux; + private WoodUsageData woodUsageData; public GamsRasterOutput(RasterSet<LandUseItem> landUses, Map<CropType, CropUsageData> cropUsageData) { super(); @@ -29,18 +30,18 @@ public class GamsRasterOutput { public GamsRasterOutput(RasterSet<LandUseItem> landUses, Map<CropType, CropUsageData> cropUsageData, Double[] woodAndCarbonProd) { this(landUses, cropUsageData); - this.woodProduction = woodAndCarbonProd[0]; + this.woodUsageData = new WoodUsageData(woodAndCarbonProd[0], 0.0); this.netCarbonFlux = woodAndCarbonProd[1]; } public GamsRasterOutput(ModelStat status, RasterSet<LandUseItem> intensityRaster, Map<CropType, CropUsageData> cropUsageData, - ArrayList<LandCoverChangeItem> gamsLandCoverChanges, Map<Integer, Double> baseTimberYield, double netCarbonFlux, double timberProduction) { + ArrayList<LandCoverChangeItem> gamsLandCoverChanges, Map<Integer, Double> baseTimberYield, double netCarbonFlux, WoodUsageData woodUsageData) { this(intensityRaster, cropUsageData); this.status = status; this.gamsLandCoverChanges = gamsLandCoverChanges; this.baseTimberYield = baseTimberYield; this.netCarbonFlux = netCarbonFlux; - this.woodProduction = timberProduction; + this.woodUsageData = woodUsageData; } public ModelStat getStatus() { @@ -67,8 +68,8 @@ public class GamsRasterOutput { return netCarbonFlux; } - public double getTimberProduction() { - return woodProduction; + public WoodUsageData getWoodUsageData() { + return woodUsageData; } } diff --git a/src/ac/ed/lurg/demand/AbstractDemandManager.java b/src/ac/ed/lurg/demand/AbstractDemandManager.java index e981c1fedbe9dd8ebed766ef5cb355b233deb13d..99f8206a5272024eff35083c73e944f16c6f7651 100644 --- a/src/ac/ed/lurg/demand/AbstractDemandManager.java +++ b/src/ac/ed/lurg/demand/AbstractDemandManager.java @@ -11,6 +11,7 @@ import ac.ed.lurg.country.SingleCountry; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; import ac.ed.lurg.types.Parameter; +import ac.ed.lurg.types.WoodType; import ac.ed.lurg.utils.LogWriter; public abstract class AbstractDemandManager { @@ -20,7 +21,7 @@ public abstract class AbstractDemandManager { protected BioenergyDemandManager bioenergyDemandManager; protected CerealFractionsManager cerealFractionsManager; protected CarbonDemandManager carbonDemandManager; - protected TimberDemandManager timberDemandManager; + protected WoodDemandManager woodDemandManager; public AbstractDemandManager(CompositeCountryManager compositeCountryManager, CalorieManager calorieManager) { this.compositeCountryManager = compositeCountryManager; @@ -28,7 +29,7 @@ public abstract class AbstractDemandManager { bioenergyDemandManager = new BioenergyDemandManager(); cerealFractionsManager = new CerealFractionsManager(compositeCountryManager); carbonDemandManager = new CarbonDemandManager(); - timberDemandManager = new TimberDemandManager(); + woodDemandManager = new WoodDemandManager(); } public Map<CommodityType, Double> getDemand(CompositeCountry cc, int year, Map<CommodityType, Double> prices, boolean outputGamsDemand) { @@ -139,8 +140,25 @@ public abstract class AbstractDemandManager { return carbonDemandManager.getGlobalCarbonDemand(timestep.getYear()); } - public double getTimberDemand(Timestep timestep) { - return timberDemandManager.getGlobalTimberDemand(timestep.getYear()); - } + protected abstract Map<WoodType, Double> getWoodDemand(SingleCountry country, int year); + + public Map<WoodType, Double> getWoodDemandComposite(CompositeCountry cc, int year) { + if (!ModelConfig.CHANGE_DEMAND_YEAR) + year = ModelConfig.BASE_YEAR; + + Map<WoodType, Double> compositeDemandMap = new HashMap<WoodType, Double>(); + Map<WoodType, Double> singleDemandMap; + + for (SingleCountry c : compositeCountryManager.getAllForCompositeCountry(cc)) { + singleDemandMap = getWoodDemand(c, year); + for (WoodType w : WoodType.values()) { + double totalDemand = (compositeDemandMap.containsKey(w)) ? compositeDemandMap.get(w) : 0.0; + double demand = singleDemandMap.get(w); + totalDemand += demand; + compositeDemandMap.put(w, totalDemand); + } + } + return compositeDemandMap; + }; } diff --git a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java index 56da459d243b29d03404694f62d347a9ad460a08..e5bd457850e6771c123551adc4c4160f7f946b85 100644 --- a/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java +++ b/src/ac/ed/lurg/demand/AbstractSSPDemandManager.java @@ -10,6 +10,7 @@ import ac.ed.lurg.country.CompositeCountryManager; import ac.ed.lurg.country.CountryManager; import ac.ed.lurg.country.SingleCountry; import ac.ed.lurg.types.CommodityType; +import ac.ed.lurg.types.WoodType; import ac.ed.lurg.utils.LogWriter; public abstract class AbstractSSPDemandManager extends AbstractDemandManager { @@ -86,4 +87,26 @@ public abstract class AbstractSSPDemandManager extends AbstractDemandManager { } abstract Map<CommodityType, Double> getFoodDemandMap(SingleCountry c, int year, double gdpPc, double population, Map<CommodityType, Double> prices, double usaGdpPc, boolean outputGamsDemand); + + protected Map<WoodType, Double> getWoodDemand(SingleCountry country, int year) { + SspData baseSd = sspManager.get(ssp_scenario, ModelConfig.BASE_YEAR, country); + SspData sd = sspManager.get(ssp_scenario, year, country); + + Map<WoodType, Double> woodDemandMap = new HashMap<WoodType, Double>(); + + if (sd == null) { + LogWriter.printlnWarning(String.format("No ssp for %s, baseYr:%d, year:%d, %s, sd:%s. Skipping", ssp_scenario, ModelConfig.BASE_YEAR, year, country.getCountryName(), sd)); + return woodDemandMap; + } + + double baseGdpPc = baseSd.getGdpPc() * ModelConfig.SSP_GDP_PC_FACTOR; + double gdpPc = sd.getGdpPc() * ModelConfig.SSP_GDP_PC_FACTOR; + double basePopulation = baseSd.getPopulation() * ModelConfig.SSP_POPULATION_FACTOR; + double population = sd.getPopulation() * ModelConfig.SSP_POPULATION_FACTOR; + + woodDemandMap = getWoodDemandMap(country, gdpPc, baseGdpPc, population, basePopulation); + return woodDemandMap; + } + + abstract Map<WoodType, Double> getWoodDemandMap(SingleCountry country, double gdpPc, double baseGdpPc, double population, double basePopulation); } diff --git a/src/ac/ed/lurg/demand/DemandManagerFromFile.java b/src/ac/ed/lurg/demand/DemandManagerFromFile.java index 7d084ec548586cb3b3587c43cb54dcdbeaf7c286..3fdad7ae9bce7447249d62b1d25cec50f00d3efc 100644 --- a/src/ac/ed/lurg/demand/DemandManagerFromFile.java +++ b/src/ac/ed/lurg/demand/DemandManagerFromFile.java @@ -7,6 +7,7 @@ import ac.ed.lurg.ModelConfig; import ac.ed.lurg.country.CompositeCountryManager; import ac.ed.lurg.country.SingleCountry; import ac.ed.lurg.types.CommodityType; +import ac.ed.lurg.types.WoodType; import ac.ed.lurg.utils.LogWriter; import ac.ed.lurg.utils.StringTabularReader; @@ -47,5 +48,11 @@ public class DemandManagerFromFile extends AbstractDemandManager { return foodDemandMap; } return dietaryAdjustment(foodDemandMap, year, calorieManager.get(c)); + } + + @Override + protected Map<WoodType, Double> getWoodDemand(SingleCountry country, int year) { + // TODO Auto-generated method stub + return null; } } \ No newline at end of file diff --git a/src/ac/ed/lurg/demand/DemandManagerSSP.java b/src/ac/ed/lurg/demand/DemandManagerSSP.java index c139c239dc86ef66afd46b17c8e0daf571155f75..833695c185481f0cedbb99678bb2665be44507c0 100644 --- a/src/ac/ed/lurg/demand/DemandManagerSSP.java +++ b/src/ac/ed/lurg/demand/DemandManagerSSP.java @@ -8,6 +8,7 @@ import ac.ed.lurg.country.CompositeCountryManager; import ac.ed.lurg.country.SingleCountry; import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.ModelFitType; +import ac.ed.lurg.types.WoodType; public class DemandManagerSSP extends AbstractSSPDemandManager { @@ -37,5 +38,12 @@ public class DemandManagerSSP extends AbstractSSPDemandManager { } return aFoodDemandMap; + } + + @Override + Map<WoodType, Double> getWoodDemandMap(SingleCountry country, double gdpPc, double baseGdpPc, double population, + double basePopulation) { + // TODO Auto-generated method stub + return null; } } \ No newline at end of file diff --git a/src/ac/ed/lurg/demand/ElasticDemandManager.java b/src/ac/ed/lurg/demand/ElasticDemandManager.java index eca6b8d87f9eebe4719c4bab89d12e2ccff64cba..e182bc6c7d804385dd5f92464653f502c28ca86f 100755 --- a/src/ac/ed/lurg/demand/ElasticDemandManager.java +++ b/src/ac/ed/lurg/demand/ElasticDemandManager.java @@ -13,6 +13,7 @@ import ac.ed.lurg.country.gams.GamsDemandInput; import ac.ed.lurg.country.gams.GamsDemandOptimiser; import ac.ed.lurg.country.gams.GamsDemandOutput; import ac.ed.lurg.types.CommodityType; +import ac.ed.lurg.types.WoodType; import ac.ed.lurg.utils.FileWriterHelper; import ac.ed.lurg.utils.LogWriter; @@ -128,4 +129,15 @@ public class ElasticDemandManager extends AbstractSSPDemandManager { LogWriter.print(e); } } + + @Override + public Map<WoodType, Double> getWoodDemandMap(SingleCountry country, double gdpPc, double baseGdpPc, double population, double basePopulation) { + Map<WoodType, Double> demandMap = new HashMap<WoodType, Double>(); + for (WoodType woodType : WoodType.values()) { + double baseDemand = woodDemandManager.get(country, woodType); + double newDemand = baseDemand * (population / basePopulation) * Math.pow(gdpPc / baseGdpPc, woodType.getIncomeDemandElasticity()); + demandMap.put(woodType, newDemand); + } + return demandMap; + } } \ No newline at end of file diff --git a/src/ac/ed/lurg/demand/TimberDemandManager.java b/src/ac/ed/lurg/demand/TimberDemandManager.java deleted file mode 100644 index 560ea3bc94548a78cf9774c33a67738fc4c363e9..0000000000000000000000000000000000000000 --- a/src/ac/ed/lurg/demand/TimberDemandManager.java +++ /dev/null @@ -1,64 +0,0 @@ -package ac.ed.lurg.demand; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.utils.Interpolator; -import ac.ed.lurg.utils.LogWriter; - -public class TimberDemandManager { - private Map<Integer, Double> globalTimberDemand; // global demand for timber - private static final int YEAR_COL = 0; - private static final int DEMAND_COL = 1; - - public TimberDemandManager() { - readTimberDemandData(); - } - - public void readTimberDemandData() { - - Map<Integer, Double> data = new HashMap<Integer, Double>(); - - String filename = ModelConfig.TIMBER_DEMAND_FILE; - try { - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - Integer year; - Double demand; - reader.readLine(); // read header - - while ((line=reader.readLine()) != null) { - String[] tokens = line.split(","); - - if (tokens.length < 2) - LogWriter.printlnError("Too few columns in " + filename + ", " + line); - - year = Integer.valueOf(tokens[YEAR_COL]); - demand = Double.valueOf(tokens[DEMAND_COL]); - - data.put(year, demand); - } - reader.close(); - - } catch (IOException e) { - LogWriter.printlnError("Failed in reading timber demand data"); - LogWriter.print(e); - } - globalTimberDemand = data; - LogWriter.println("Processed " + filename); - } - - public double getGlobalTimberDemand(int year) { - int downYear = (year/5) * 5; - int upYear = downYear + 5; - Double lowerD = globalTimberDemand.get(downYear); - Double upperD = globalTimberDemand.get(upYear); - double factor = ((double)(year - downYear)) / (upYear - downYear); - Double d = Interpolator.interpolate(lowerD, upperD, factor); - return d; - } -} diff --git a/src/ac/ed/lurg/demand/WoodDemandManager.java b/src/ac/ed/lurg/demand/WoodDemandManager.java new file mode 100644 index 0000000000000000000000000000000000000000..b95093a3a91b6191920da7fb47e5203067732031 --- /dev/null +++ b/src/ac/ed/lurg/demand/WoodDemandManager.java @@ -0,0 +1,81 @@ +package ac.ed.lurg.demand; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.country.CountryManager; +import ac.ed.lurg.country.SingleCountry; +import ac.ed.lurg.types.WoodType; +import ac.ed.lurg.utils.LogWriter; + +public class WoodDemandManager { + private static final int COUNTY_COL = 0; + private static final int WOOD_TYPE_COL = 1; + private static final int DEMAND_COL = 2; + private static final double WOOD_BIOMASS_CONVERSION_FACTOR = 0.2688; // m3 to tC-eq p.16 [https://doi.org/10.5194/gmd-13-5425-2020] + + private Map<SingleCountry, Map<WoodType, Double>> baseWoodDemand = new HashMap<SingleCountry, Map<WoodType, Double>>(); + + public WoodDemandManager() { + readWoodDemandData(); + } + + public void readWoodDemandData() { + + String filename = ModelConfig.TIMBER_DEMAND_FILE; + try { + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line, countryName, woodTypeName; + Double demand; + reader.readLine(); // read header + + while ((line=reader.readLine()) != null) { + String[] tokens = line.split(","); + + if (tokens.length < 3) + LogWriter.printlnError("Too few columns in " + filename + ", " + line); + + countryName = tokens[COUNTY_COL]; + woodTypeName = tokens[WOOD_TYPE_COL]; + demand = Double.valueOf(tokens[DEMAND_COL]); + + SingleCountry country = CountryManager.getForName(countryName); + if (country == null) + LogWriter.printlnError("Null country for" + countryName); + else { + WoodType woodType = WoodType.getForName(woodTypeName); + + Map<WoodType, Double> woodTypeMap = baseWoodDemand.get(country); + if (woodTypeMap == null) { + woodTypeMap = new HashMap<WoodType, Double>(); + baseWoodDemand.put(country, woodTypeMap); + } + + double demandBiomassEq = demand / 1e6 * WOOD_BIOMASS_CONVERSION_FACTOR; // converting from m3 to MtC-eq + woodTypeMap.put(woodType, demandBiomassEq); + } + } + reader.close(); + + } catch (IOException e) { + LogWriter.printlnError("Failed in reading wood demand data"); + LogWriter.print(e); + } + LogWriter.println("Processed " + filename); + } + + public double get(SingleCountry country, WoodType woodType) { + Map<WoodType, Double> woodTypeMap = baseWoodDemand.get(country); + if (woodTypeMap != null && woodTypeMap.containsKey(woodType)) { + return woodTypeMap.get(woodType).doubleValue(); + } else { + LogWriter.printlnError("BaseConsumpManager: can't get value for " + woodType + ", " + country); + return 0.0; + } + } + +} diff --git a/src/ac/ed/lurg/landuse/WoodUsageData.java b/src/ac/ed/lurg/landuse/WoodUsageData.java new file mode 100644 index 0000000000000000000000000000000000000000..2232acd97c23aa74aad946248fa87bbff2a48eb3 --- /dev/null +++ b/src/ac/ed/lurg/landuse/WoodUsageData.java @@ -0,0 +1,19 @@ +package ac.ed.lurg.landuse; + +public class WoodUsageData { + private double harvest; + private double netImport; + + public WoodUsageData(double harvest, double netImport) { + this.harvest = harvest; + this.netImport = netImport; + } + + public double getHarvest() { + return harvest; + } + + public double getNetImport() { + return netImport; + } +} diff --git a/src/ac/ed/lurg/types/WoodType.java b/src/ac/ed/lurg/types/WoodType.java new file mode 100644 index 0000000000000000000000000000000000000000..de4af65fa1e2b7e927146b6f03b68dbc4527a111 --- /dev/null +++ b/src/ac/ed/lurg/types/WoodType.java @@ -0,0 +1,43 @@ +package ac.ed.lurg.types; + +import java.util.HashMap; +import java.util.Map; +import ac.ed.lurg.ModelConfig; + +public enum WoodType { + ROUNDWOOD("roundwood", ModelConfig.ROUNDWOOD_DEMAND_ELASTICITY), + FUELWOOD("fuelwood", ModelConfig.FUELWOOD_DEMAND_ELASTICITY); + + private String name; + private double incomeDemandElasticity; + + WoodType(String name, double incomeDemandElasticity) { + this.name = name; + this.incomeDemandElasticity = incomeDemandElasticity; + } + + private static final Map<String, WoodType> nameCache = new HashMap<String, WoodType>(); + + static { + for (WoodType w : values()) { + nameCache.put(w.getName(), w); + } + } + + public static WoodType getForName(String name) { + WoodType woodType = nameCache.get(name); + + if (woodType == null) + throw new RuntimeException("Can't find woodType for name: " + name); + + return woodType; + } + + public String getName() { + return name; + } + + public double getIncomeDemandElasticity() { + return incomeDemandElasticity; + } +}