diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index 8cb8be40f6dead5bf35ed696064351c5d126d7d4..41da003d1f408bf67e40470782b917c4d3df10e4 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -135,13 +135,16 @@ public class ModelConfig { public static final String TEMP_DIR = getProperty("TEMP_DIR", OUTPUT_DIR + File.separator + "GamsTmp"); public static final String DATA_DIR = getProperty("DATA_DIR", BASE_DIR + File.separator + "data"); public static final String GAMS_DIR = getProperty("GAMS_DIR", BASE_DIR + File.separator + "GAMS"); - public static final boolean CLEANUP_GAMS_DIR = getBooleanProperty("CLEANUP_GAMS_DIR", false); + public static final boolean CLEANUP_GAMS_DIR = getBooleanProperty("CLEANUP_GAMS_DIR", true); public static final boolean ORIG_LEAST_COST_MIN = getBooleanProperty("ORIG_LEAST_COST_MIN", true); public static final String GAMS_MODEL_NAME = getProperty("GAMS_MODEL_NAME", ORIG_LEAST_COST_MIN==true ? "IntExtOpt.gms" : "LUOpt.gms"); public static final String GAMS_MODEL = getProperty("GAMS_MODEL", GAMS_DIR + File.separator + GAMS_MODEL_NAME); public static final String DEMAND_GAMS_MODEL = getProperty("DEMAND_GAMS_MODEL", GAMS_DIR + File.separator + "elasticDemand.gms"); public static final String DEMAND_PARAM_FILE = getProperty("DEMAND_PARAM_FILE", DATA_DIR + File.separator + "DemandParamConv.gdx"); + + // Runtime options + public static final int MULTITHREAD_NUM = getIntProperty("MULTITHREAD_NUM", 4); // number of parallel threads // Country (non-gridded) data public static final boolean DEMAND_FROM_FILE = getBooleanProperty("DEMAND_FROM_FILE", false); // used in hindcasting @@ -233,7 +236,8 @@ public class ModelConfig { public static final String FPU_GROUPING_FILE = SPATIAL_DATA_DIR + File.separator + "fpuGrouping.txt"; public static final String IRRIG_MAX_WATER_FILENAME = getProperty("IRRIG_MAX_WATER_FILENAME", "gsirrigation.out"); public static final String IRRIG_RUNOFF_FILE = getProperty("IRRIG_RUNOFF_FILE", "tot_runoff.out"); - public static final String PROTECTED_AREAS_FILE = getProperty("PROTECTED_AREAS_FILE",SPATIAL_DATA_DIR + File.separator + "protectedAreas2019.asc"); + public static final String PROTECTED_AREAS_FILENAME = getProperty("PROTECTED_AREAS_FILENAME","protectedAreas2019.asc"); + public static final String PROTECTED_AREAS_FILE = getProperty("PROTECTED_AREAS_FILE",SPATIAL_DATA_DIR + File.separator + PROTECTED_AREAS_FILENAME); public static final String HALF_EARTH_FILE = getProperty("HALF_EARTH_FILE",SPATIAL_DATA_DIR + File.separator + "global_biodiversity_priorities50perc.asc"); public static final String HIGH_SLOPE_AREAS_FILE = SPATIAL_DATA_DIR + File.separator + "maxcropfrac2.txt"; public static final String YIELDSHOCK_MAP_DIR = SPATIAL_DATA_DIR + File.separator + "yieldshockmaps"; @@ -255,8 +259,8 @@ public class ModelConfig { public static final String LAND_COVER_AGE_DIST_FILENAME = SPATIAL_DATA_DIR + File.separator + "land_cover_age_dist.txt"; public static final int LAND_COVER_INIT_AGE_GROUP_SIZE = getIntProperty("CARBON_WOOD_AGE_CLASSES", 10); // years public static final int WOOD_AND_CARBON_TIMESTEP_SIZE = getIntProperty("WOOD_AND_CARBON_TIMESTEP_SIZE", 20); // years - public static final double WOOD_YIELD_CALIB_FACTOR = getDoubleProperty("WOOD_YIELD_CALIB_FACTOR", 6.0); - public static final int CARBON_WOOD_MAX_TIME = getIntProperty("CARBON_WOOD_AGE_CLASSES", 165); + public static final double WOOD_YIELD_CALIB_FACTOR = getDoubleProperty("WOOD_YIELD_CALIB_FACTOR", 1.0); + public static final int CARBON_WOOD_MAX_TIME = getIntProperty("CARBON_WOOD_MAX_TIME", 165); // Output public static final String LAND_COVER_OUTPUT_FILE = OUTPUT_DIR + File.separator + "lc.txt"; @@ -291,6 +295,7 @@ public class ModelConfig { public static final String CHECKPOINT_INTERNATIONAL_MARKET_FILE = getProperty("CHECKPOINT_INTERNATIONAL_MARKET_FILE", OUTPUT_DIR + File.separator + SERIALIZED_INTERNATIONAL_MARKET_FILENAME); public static final String CHECKPOINT_LAND_COVER_FILE = getProperty("CHECKPOINT_LAND_COVER_FILE", OUTPUT_DIR + File.separator + SERIALIZED_LAND_COVER_FILENAME); public static final String CHECKPOINT_WOOD_USAGE_FILE = getProperty("CHECKPOINT_WOOD_USAGE_FILE", OUTPUT_DIR + File.separator + SERIALIZED_WOOD_USAGE_FILENAME); + public static final boolean DISABLE_SERIALIZATION = getBooleanProperty("DISABLE_SERIALIZATION", false); public static final boolean MARKET_ADJ_PRICE = getBooleanProperty("MARKET_ADJ_PRICE", true); public static final boolean CHANGE_YIELD_DATA_YEAR = IS_CALIBRATION_RUN ? false : getBooleanProperty("CHANGE_YIELD_DATA_YEAR", true); @@ -346,9 +351,7 @@ public class ModelConfig { public static final double PASTURE_DECREASE_COST = getDoubleProperty("PASTURE_DECREASE_COST", LAND_CHANGE_COST); public static final double CROP_DECREASE_COST = getDoubleProperty("CROP_DECREASE_COST", 1.65 * LAND_CHANGE_COST); public static final double PASTURE_INCREASE_COST = getDoubleProperty("PASTURE_INCREASE_COST", 0.35 * LAND_CHANGE_COST * CROP_TO_PASTURE_COST_FACTOR * AGRI_LAND_EXPANSION_COST_FACTOR); - public static final double AGRI_EXPANSION_COST_BASE = getDoubleProperty("AGRI_EXPANSION_COST_BASE", 0.03 * LAND_CHANGE_COST * AGRI_LAND_EXPANSION_COST_FACTOR); - 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.5 * LAND_CHANGE_COST); // $1000/ha + public static final double MANAGED_FOREST_INCREASE_COST = getDoubleProperty("MANAGED_FOREST_INCREASE_COST", 2 * LAND_CHANGE_COST); // $1000/ha public static final double TECHNOLOGY_CHANGE_ANNUAL_RATE = getDoubleProperty("TECHNOLOGY_CHANGE_ANNUAL_RATE", 0.002); public static final int TECHNOLOGY_CHANGE_START_STEP = getIntProperty("TECHNOLOGY_CHANGE_START_STEP", 0); @@ -420,6 +423,7 @@ public class ModelConfig { public static final long RANDOM_SEED = getIntProperty("RANDOM_SEED", 1974329); // any number will do public static final String CHECKPOINT_YEARS = getProperty("CHECKPOINT_YEARS", null); // 2020,2050 + public static final boolean SERIALIZE_FINAL_TIMESTEP_ONLY = getBooleanProperty("SERIALIZE_FINAL_TIMESTEP_ONLY", false); // Protected areas forcing parameters public static final boolean FORCE_PROTECTED_AREAS = IS_CALIBRATION_RUN ? false : getBooleanProperty("FORCE_PROTECTED_AREAS", false); @@ -444,28 +448,29 @@ public class ModelConfig { public static final String MIN_MAX_TRADE_FILE = getProperty("MIN_MAX_TRADE_FILE", OUTPUT_DIR + File.separator + "minMaxNetImport.csv"); // Forestry and carbon market parameters - public static final String CONVERSION_COST_FILE = DATA_DIR + File.separator + "conversion_costs.csv"; // cost of converting from one land cover to another, $1000/ha + public static final String CONVERSION_COST_FILE = getProperty("CONVERSION_COST_FILE", DATA_DIR + File.separator + "conversion_costs.csv"); // cost of converting from one land cover to another, $1000/ha public static final String CARBON_DEMAND_FILENAME = getProperty("CARBON_DEMAND_FILENAME", "carbon_demand.csv"); public static final String CARBON_DEMAND_FILE = getProperty("CARBON_DEMAND_FILE", DATA_DIR + File.separator + CARBON_DEMAND_FILENAME); - public static final String TIMBER_DEMAND_FILENAME = getProperty("TIMBER_DEMAND_FILENAME", "timber_demand.csv"); - public static final String TIMBER_DEMAND_FILE = getProperty("TIMBER_DEMAND_FILE", DATA_DIR + File.separator + TIMBER_DEMAND_FILENAME); + public static final String WOOD_DEMAND_FILENAME = getProperty("WOOD_DEMAND_FILENAME", "wood_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", 500.0); // tC-eq public static final double IND_ROUNDWOOD_DEMAND_ELASTICITY = getDoubleProperty("IND_ROUNDWOOD_DEMAND_ELASTICITY", 0.3123881); public static final double FUELWOOD_DEMAND_ELASTICITY = getDoubleProperty("FUELDWOOD_DEMAND_ELASTICITY", -0.3598551); - public static final String WOOD_NET_IMPORTS_FILENAME = getProperty("WOOD_NET_IMPORTS_FILENAME", "wood_net_imports.csv"); - public static final String WOOD_NET_IMPORTS_FILE = getProperty("WOOD_NET_IMPORTS_FILE", DATA_DIR + File.separator + WOOD_NET_IMPORTS_FILENAME); public static final double WOOD_BIOMASS_CONVERSION_FACTOR = getDoubleProperty("WOOD_BIOMASS_CONVERSION_FACTOR", 3e-7); // m3 to MtC-eq p.16 [https://doi.org/10.5194/gmd-13-5425-2020] public static final double DISCOUNT_RATE = getDoubleProperty("DISCOUNT_RATE", 0.07); public static final double VEGETATION_CLEARING_COST = getDoubleProperty("VEGETATION_CLEARING_COST", 0.005); //$1000/tC public static final boolean CONVERSION_COSTS_FROM_FILE = getBooleanProperty("CONVERSION_COSTS_FROM_FILE", false); public static final boolean IS_FORESTRY_ON = getBooleanProperty("IS_FORESTRY_ON", true); public static final boolean IS_CARBON_ON = getBooleanProperty("IS_CARBON_ON", true); - public static final double FOREST_ESTABLISHMENT_COST = IS_FORESTRY_ON ? getDoubleProperty("FOREST_ESTABLISHMENT_COST", 2.0) : 0.0; // $1000/ha + public static final double FOREST_ESTABLISHMENT_COST = IS_FORESTRY_ON ? getDoubleProperty("FOREST_ESTABLISHMENT_COST", 8.0) : 0.0; // $1000/ha public static final int CARBON_HORIZON = getIntProperty("CARBON_HORIZON", 30); - public static final double WOOD_TRADE_BARRIER = getDoubleProperty("WOOD_TRADE_BARRIER", 0.05); //$1000/tC + public static final double WOOD_TRADE_BARRIER = getDoubleProperty("WOOD_TRADE_BARRIER", 0.2); //$1000/tC public static final double INIT_CARBON_PRICE = IS_CARBON_ON ? getDoubleProperty("INIT_CARBON_PRICE", 0.02) : 0.0; // $1000/tC-eq - public static final double INIT_WOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_WOOD_PRICE", 0.4) : 0.0; // $1000/tC-eq + public static final double INIT_WOOD_PRICE = IS_FORESTRY_ON ? getDoubleProperty("INIT_WOOD_PRICE", 0.387) : 0.0; // $1000/tC-eq public static final boolean RESET_ENERGYCROP_PRICE = getBooleanProperty("RESET_ENERGYCROP_PRICE", true); - public static final boolean ENABLE_VEGETATION_CLEARANCE_COST = getBooleanProperty("ENABLE_VEGETATION_CLEARANCE_COST", true); + public static final boolean ENABLE_VEGETATION_CLEARANCE_COST = getBooleanProperty("ENABLE_VEGETATION_CLEARANCE_COST", false); + // When forestry enabled, conversion cost based on amount of biomass cleared + public static final double MANAGED_FOREST_DECREASE_COST = IS_FORESTRY_ON || ENABLE_VEGETATION_CLEARANCE_COST ? getDoubleProperty("MANAGED_FOREST_DECREASE_COST", 0.1 * LAND_CHANGE_COST) : getDoubleProperty("MANAGED_FOREST_DECREASE_COST", 0.5 * LAND_CHANGE_COST); // $1000/ha + public static final String HIST_WOOD_CONSUMP_FILE = getProperty("HIST_WOOD_CONSUMP_FILE", DATA_DIR + File.separator + "hist_wood_consumption.csv"); } diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java index 3ff119d4bf124eb5cfd0a80b2b8b293c58bbd31d..b163d0fc0a0f45c742df1cc032502e07cd7e8d0c 100644 --- a/src/ac/ed/lurg/ModelMain.java +++ b/src/ac/ed/lurg/ModelMain.java @@ -190,7 +190,8 @@ public class ModelMain { // output results outputTimestepResults(timestep); - checkAndSaveCheckpoint(timestep); + if (((ModelConfig.SERIALIZE_FINAL_TIMESTEP_ONLY && timestep.getTimestep() == ModelConfig.END_TIMESTEP) || !ModelConfig.SERIALIZE_FINAL_TIMESTEP_ONLY) && !ModelConfig.DISABLE_SERIALIZATION) + checkAndSaveCheckpoint(timestep); } @@ -402,7 +403,7 @@ public class ModelMain { private void writeWoodAndCarbonProdFile(Timestep timestep) { try { - StringBuffer sbHeadings = new StringBuffer("Year, Country, Item, Production, Import_price, Export_price, Net_imports,"); + StringBuffer sbHeadings = new StringBuffer("Year, Country, Item, Production, Import_price, Export_price, Net_imports"); BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.WOOD_CARBON_OUTPUT_FILE, sbHeadings.toString()); for (AbstractCountryAgent country : countryAgents.getAll()) { diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java index 7a1b60aa56cc44aa9cb81c55a82b4bfa469b923b..686315598475c85741416c2dcd79953c1ad89bf3 100644 --- a/src/ac/ed/lurg/country/CountryAgent.java +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -1,10 +1,5 @@ package ac.ed.lurg.country; -import java.io.File; -import java.io.IOException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -18,6 +13,7 @@ import ac.ed.lurg.country.gams.GamsRasterOptimiser; import ac.ed.lurg.country.gams.GamsRasterOutput; import ac.ed.lurg.demand.AbstractDemandManager; import ac.ed.lurg.forestry.WoodYieldItem; +import ac.ed.lurg.forestry.WoodYieldRasterSet; import ac.ed.lurg.landuse.CropUsageData; import ac.ed.lurg.landuse.IrrigationItem; import ac.ed.lurg.landuse.LandUseItem; @@ -47,8 +43,6 @@ public class CountryAgent extends AbstractCountryAgent { private Map<CropType, Double> subsidyRates; private Map<Integer, Map<CropType, Double>> exportRestrictions; private Map<Integer, Map<LimitType, Map<CropType, Double>>> minMaxTradeLimits; - private boolean saveGamsGdxFiles; - public CountryAgent(AbstractDemandManager demandManager,CompositeCountry country, RasterSet<LandUseItem> cropAreaRaster, Map<CropType, CropUsageData> cropUsageData, Map<CropType, Double> tradeBarriers, RasterSet<IntegerRasterItem> yieldClusters, Map<CropType, Double> subsidyRates, Map<WoodType, WoodUsageData> countryWoodData, @@ -62,8 +56,6 @@ public class CountryAgent extends AbstractCountryAgent { GamsRasterOutput initialData = new GamsRasterOutput(cropAreaRaster, cropUsageData, countryWoodData); previousGamsRasterOutput = initialData; - - saveGamsGdxFiles = (ModelConfig.GAMS_COUNTRY_TO_SAVE != null && country.getName().equals(ModelConfig.GAMS_COUNTRY_TO_SAVE)); } public RasterSet<IntegerRasterItem> getYieldClusters() { @@ -126,9 +118,6 @@ public class CountryAgent extends AbstractCountryAgent { savePreviousProducerCropPrices(); } - if (saveGamsGdxFiles && ModelConfig.PRICE_ELASTIC_DEMAND) - saveGDXFile("demand"); - if (currentProjectedDemand.size() == 0) { LogWriter.printlnError("No demand for country " + country + " so skipping it"); } @@ -150,9 +139,6 @@ public class CountryAgent extends AbstractCountryAgent { GamsRasterOutput result = opti.run(); - if (saveGamsGdxFiles) - saveGDXFile("landuse"); - previousGamsRasterOutput = result; if (!ModelConfig.IS_CALIBRATION_RUN) @@ -170,22 +156,6 @@ public class CountryAgent extends AbstractCountryAgent { updateNetImportsFromProdAndDemand(currentProjectedDemand, currentDemandFract, previousGamsRasterOutput.getCropUsageData()); } - private void saveGDXFile(String ext) { - // some hacky code for debug purposes that keeps each gams gdx file - try { - Files.copy( - FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + "_gams_java_gdb2.gdx"), - FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + country.getName().replaceAll("\\s+","") + currentTimestep.getYear() + ext + ".gdx"), - StandardCopyOption.REPLACE_EXISTING); - Files.copy( - FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + "_gams_java_gjo1.lst"), - FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + country.getName().replaceAll("\\s+","") + currentTimestep.getYear() + ext + "Listing" + ".lst"), - StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - LogWriter.print(e); - } - } - private GamsRasterInput getGamsRasterInput(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces, RasterSet<WoodYieldItem> woodYieldData, RasterSet<CarbonFluxItem> carbonFluxData, Map<LccKey, Double> conversionCosts, double carbonDemandIncrease) { @@ -193,8 +163,10 @@ public class CountryAgent extends AbstractCountryAgent { if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION) { // initialisation time-step allowedImportChange = 0.0; - } - else { // normal (not the initial) time-step + } else if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() > ModelConfig.END_FIRST_STAGE_CALIBRATION) { + double cooldownFactor = 4 / (1 + Math.exp((currentTimestep.getTimestep() - 20) * 0.5)) + 1; + allowedImportChange = ModelConfig.MAX_IMPORT_CHANGE * cooldownFactor; // gradually decrease allowed import change + } else { // normal (not the initial) time-step allowedImportChange = ModelConfig.MAX_IMPORT_CHANGE; // when running is calibration model calibrate (ModelConfig.IS_CALIBRATION_RUN==true) MAX_IMPORT_CHANGE is already 0. } @@ -251,8 +223,10 @@ public class CountryAgent extends AbstractCountryAgent { // Timber import/export constraints Map<WoodType, TradeConstraint> woodTradeConstraints = new HashMap<WoodType, TradeConstraint>(); + Map<WoodType, WoodUsageData> woodUsageData = previousGamsRasterOutput.getWoodUsageData(); + double totalWoodProd = woodUsageData.values().stream().mapToDouble(o -> o.getHarvest()).reduce(0.0, Double::sum); - for (Map.Entry<WoodType, WoodUsageData> entry : previousGamsRasterOutput.getWoodUsageData().entrySet()) { + for (Map.Entry<WoodType, WoodUsageData> entry : woodUsageData.entrySet()) { WoodType woodType = entry.getKey(); if (!ModelConfig.IS_FORESTRY_ON) { woodTradeConstraints.put(woodType, new TradeConstraint(0, 0)); // No exports or imports if forestry off @@ -263,13 +237,16 @@ public class CountryAgent extends AbstractCountryAgent { double baseTrade = woodUsage.getNetImport(); // Make sure country can import sufficient wood + // Some countries produce wood from irrigated forests e.g. Egypt which are not modelled here. + // Therefore we need to adjust the initial FAO imports/exports. + if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.isInitialTimestep()) { - // assume 1tC/ha/year - double potentialYield = LandUseItem.getTotalLandCover(previousGamsRasterOutput.getLandUses().values(), LandCoverType.TIMBER_FOREST); + double prodFract = woodUsage.getHarvest() / totalWoodProd; + double potentialMaxYield = WoodYieldRasterSet.getMaxWoodHarvest(woodYieldData, previousGamsRasterOutput.getLandUses()); + double potentialAdjYield = potentialMaxYield * prodFract * 0.5; // adjust based on demand and cap at max 50% realised double currentDemand = currentWoodDemand.get(woodType); - double importsNeeded = Math.max(0, currentDemand - potentialYield * 3); + double importsNeeded = Math.max(0, currentDemand - potentialAdjYield); baseTrade = importsNeeded > 0 ? importsNeeded : baseTrade; - baseTrade = baseTrade < 0 ? baseTrade * 20 : baseTrade; } double changeUp = 0.0; diff --git a/src/ac/ed/lurg/country/CountryAgentManager.java b/src/ac/ed/lurg/country/CountryAgentManager.java index 257b80ee544c6d4c7a0353d7128070f5898d14d0..31412acadd7c1d64371c0d5280c124174f329bb8 100644 --- a/src/ac/ed/lurg/country/CountryAgentManager.java +++ b/src/ac/ed/lurg/country/CountryAgentManager.java @@ -8,6 +8,9 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import ac.ed.lurg.InternationalMarket; import ac.ed.lurg.ModelConfig; @@ -117,11 +120,13 @@ public class CountryAgentManager { public void determineProductionForAll(Timestep timestep, YieldRaster yieldSurfaces, IrrigationRasterSet currentIrrigationData, CarbonFluxRasterSet currentCarbonFluxData, WoodYieldRasterSet currentWoodYieldData, Map<LccKey, Double> conversionCosts, double carbonDemandIncrease) { - + for (AbstractCountryAgent aca : countryAgents) { aca.setCurrentTimestep(timestep); } - + + ExecutorService execService = Executors.newFixedThreadPool(ModelConfig.MULTITHREAD_NUM); + for (CountryAgent ca : gamsCountryAgents) { LogWriter.println("Country " + ca.getCountry()); Collection<RasterKey> countryKeys = countryBoundaryRaster.getKeysFor(ca.getCountry()); @@ -129,24 +134,36 @@ public class CountryAgentManager { RasterSet<IrrigationItem> irrigData = currentIrrigationData.createSubsetForKeys(countryKeys); RasterSet<CarbonFluxItem> carbonFluxData = currentCarbonFluxData.createSubsetForKeys(countryKeys); RasterSet<WoodYieldItem> woodYieldData = currentWoodYieldData.createSubsetForKeys(countryKeys); - - try { - ca.determineProduction(countryYieldSurfaces, irrigData, internationalMarket.getWorldPrices(), carbonFluxData, woodYieldData, - conversionCosts, carbonDemandIncrease, internationalMarket.getCarbonPrice(), internationalMarket.getWoodPrice()); - - // update global rasters - globalLandUseRaster.putAll(ca.getLandUses()); - // if first timestep and calibration get the clustering info, which doesn't change through time - if (ModelConfig.GENERATE_NEW_YIELD_CLUSTERS && timestep.isInitialTimestep()) - clusterIdRaster.putAll(ca.getYieldClusters()); - - } catch (Exception e) { - LogWriter.printlnError("Exception processing " + ca.getCountry() + " will continue with other countries"); - LogWriter.print(e); - } + execService.execute(new Runnable() { + @Override + public void run() { + + try { + ca.determineProduction(countryYieldSurfaces, irrigData, internationalMarket.getWorldPrices(), carbonFluxData, woodYieldData, + conversionCosts, carbonDemandIncrease, internationalMarket.getCarbonPrice(), internationalMarket.getWoodPrice()); + + // update global rasters + globalLandUseRaster.putAll(ca.getLandUses()); + + // if first timestep and calibration get the clustering info, which doesn't change through time + if (ModelConfig.GENERATE_NEW_YIELD_CLUSTERS && timestep.isInitialTimestep()) + clusterIdRaster.putAll(ca.getYieldClusters()); + } catch (Exception e) { + LogWriter.printlnError("Exception processing " + ca.getCountry() + " will continue with other countries"); + } + + } + }); } - + + try { + execService.shutdown(); + execService.awaitTermination(30, TimeUnit.MINUTES); + } catch (InterruptedException e) { + LogWriter.printlnError(e.toString()); + } + if (craftyCountryAgents.size() > 0) { craftyManager.updateWithCraftyData(craftyCountryAgents, timestep, internationalMarket.getWorldPrices(), internationalMarket.getCarbonPrice(), internationalMarket.getWoodPrice()); // this will wait for the marker file from CRAFTY diff --git a/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java b/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java index c107b32939c5402e91b8e1c380d0f003e560ede7..823bcc2153ad6c71e1262231e56777ac4ef2a15e 100755 --- a/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java @@ -1,6 +1,10 @@ package ac.ed.lurg.country.gams; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -31,10 +35,19 @@ public class GamsDemandOptimiser { } public GamsDemandOutput getDemandPc() { - File workingDirectory = new File(ModelConfig.TEMP_DIR); - workingDirectory.mkdir(); + File tempDir = new File(ModelConfig.TEMP_DIR); + tempDir.mkdir(); + + Path workingDirectory = null; + try { + workingDirectory = Files.createTempDirectory(Paths.get(ModelConfig.TEMP_DIR), "dem"); + } catch (IOException e) { + // TODO Auto-generated catch block + LogWriter.printlnError(e.toString()); + } + //File workingDirectory = new File(ModelConfig.TEMP_DIR); GAMSWorkspaceInfo wsInfo = new GAMSWorkspaceInfo(); - wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath()); + wsInfo.setWorkingDirectory(workingDirectory.toAbsolutePath().toString()); GAMSWorkspace ws = new GAMSWorkspace(wsInfo); GAMSOptions opt = ws.addOptions(); @@ -62,7 +75,15 @@ public class GamsDemandOptimiser { gamsJob.run(opt, dbs.toArray(new GAMSDatabase[dbs.size()])); LogWriter.println("Took " + (System.currentTimeMillis() - startTime) + " ms to run"); - return handleResults(gamsJob.OutDB()); + + GamsDemandOutput gamsOutput = handleResults(gamsJob.OutDB()); + + if (ModelConfig.CLEANUP_GAMS_DIR) { + gamsJob.OutDB().dispose(); + deleteDirectory(workingDirectory.toFile()); + } + + return(gamsOutput); } private void adjustDietParams(GAMSDatabase adjustedParamDb) { @@ -177,4 +198,15 @@ public class GamsDemandOptimiser { return new GamsDemandOutput(status, demandMap, utility, desiredConsumpFactor); } + + + boolean deleteDirectory(File dir) { + File[] dirContents = dir.listFiles(); + if (dirContents != null) { + for (File file : dirContents) { + deleteDirectory(file); + } + } + return dir.delete(); + } } diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index 85208f09f0da4061d5115ab9fa24402ae0c761b5..cf1d88a43a7976782ebcb0a6cd0b27be561e92c3 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -1,6 +1,12 @@ package ac.ed.lurg.country.gams; import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -46,20 +52,29 @@ import ac.ed.lurg.yield.YieldResponsesItem; public class GamsLocationOptimiser { private static final boolean DEBUG = true; + private boolean saveGamsGdxFiles; private GamsLocationInput inputData; public GamsLocationOptimiser(GamsLocationInput inputData) { this.inputData = inputData; + saveGamsGdxFiles = (ModelConfig.GAMS_COUNTRY_TO_SAVE != null && inputData.getCountryInput().getCountry().getName().equals(ModelConfig.GAMS_COUNTRY_TO_SAVE)); } public GamsLocationOutput run() { + File tempDir = new File(ModelConfig.TEMP_DIR); + tempDir.mkdir(); - File workingDirectory = new File(ModelConfig.TEMP_DIR); - workingDirectory.mkdir(); + Path workingDirectory = null; + try { + workingDirectory = Files.createTempDirectory(Paths.get(ModelConfig.TEMP_DIR), "loc"); + } catch (IOException e) { + // TODO Auto-generated catch block + LogWriter.printlnError(e.toString()); + } GAMSWorkspaceInfo wsInfo = new GAMSWorkspaceInfo(); - wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath()); + wsInfo.setWorkingDirectory(workingDirectory.toAbsolutePath().toString()); // wsInfo.setDebugLevel(DebugLevel.VERBOSE); GAMSWorkspace ws = new GAMSWorkspace(wsInfo); @@ -77,12 +92,19 @@ public class GamsLocationOptimiser { long startTime = System.currentTimeMillis(); gamsJob.run(opt, inDB); - if (ModelConfig.CLEANUP_GAMS_DIR) - cleanup(ws.workingDirectory()); - LogWriter.println("Took " + (System.currentTimeMillis() - startTime) + " ms to run"); - return handleResults(gamsJob.OutDB()); + GamsLocationOutput gamsOutput = handleResults(gamsJob.OutDB()); + + if (saveGamsGdxFiles) + saveGDXFile("landuse", workingDirectory.toFile()); + + if (ModelConfig.CLEANUP_GAMS_DIR) { + gamsJob.OutDB().dispose(); + deleteDirectory(workingDirectory.toFile()); + } + + return(gamsOutput); } private void setupInDB(GAMSDatabase inDB) { @@ -623,21 +645,30 @@ public class GamsLocationOptimiser { } } - - private void cleanup(String directory) { - File directoryToDelete = new File(directory); - String files[] = directoryToDelete.list(); - for (String file : files) { - File fileToDelete = new File(directoryToDelete, file); - try { - fileToDelete.delete(); - } catch(Exception e){ - LogWriter.print(e); - } - } + boolean deleteDirectory(File dir) { + File[] dirContents = dir.listFiles(); + if (dirContents != null) { + for (File file : dirContents) { + deleteDirectory(file); + } + } + return dir.delete(); + } + + private void saveGDXFile(String ext, File tempDir) { + // some hacky code for debug purposes that keeps each gams gdx file + String country = inputData.getCountryInput().getCountry().getName().replaceAll("\\s+",""); + int year = inputData.getTimestep().getYear(); try { - directoryToDelete.delete(); - } catch(Exception e) { + Files.copy( + FileSystems.getDefault().getPath(tempDir + File.separator + "_gams_java_gdb2.gdx"), + FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + country + year + ext + ".gdx"), + StandardCopyOption.REPLACE_EXISTING); + Files.copy( + FileSystems.getDefault().getPath(tempDir + File.separator + "_gams_java_gjo1.lst"), + FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + country + year + ext + "Listing" + ".lst"), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { LogWriter.print(e); } } diff --git a/src/ac/ed/lurg/demand/BioenergyDemandManager.java b/src/ac/ed/lurg/demand/BioenergyDemandManager.java index c01e0cbf273251deae81e35755c09103ef653921..627186c5d741e295893c43ffadbf73606489db8a 100644 --- a/src/ac/ed/lurg/demand/BioenergyDemandManager.java +++ b/src/ac/ed/lurg/demand/BioenergyDemandManager.java @@ -169,7 +169,10 @@ public class BioenergyDemandManager { double bioenergyDemandAdj = 0; if (ModelConfig.USE_BIOENERGY_TRAJECTORY) { - bioenergyDemandAdj = interpolateDemand(firstGenGlobalMass, year, 5) / firstGenGlobalMass.get(ModelConfig.BASE_YEAR); + double globalBaseDemand = firstGenGlobalMass.get(ModelConfig.BASE_YEAR); + if (globalBaseDemand > 0) { + bioenergyDemandAdj = interpolateDemand(firstGenGlobalMass, year, 5) / firstGenGlobalMass.get(ModelConfig.BASE_YEAR); + } } else { int yearsOfChange = Math.min(ModelConfig.BIOENERGY_CHANGE_END_YEAR - ModelConfig.BIOENERGY_CHANGE_START_YEAR, year - ModelConfig.BIOENERGY_CHANGE_START_YEAR); diff --git a/src/ac/ed/lurg/demand/DemandManagerFromFile.java b/src/ac/ed/lurg/demand/DemandManagerFromFile.java index d9a3c2ecc880f638c4657f5075b87f01807b3ecb..a50b3555db120e03df92157d114dab7f09e5a3b3 100644 --- a/src/ac/ed/lurg/demand/DemandManagerFromFile.java +++ b/src/ac/ed/lurg/demand/DemandManagerFromFile.java @@ -15,11 +15,15 @@ import ac.ed.lurg.utils.StringTabularReader; public class DemandManagerFromFile extends AbstractDemandManager { private StringTabularReader historicalConsumptionReader; + private StringTabularReader histWoodConsumpReader; public DemandManagerFromFile(CompositeCountryManager compositeCountryManager, CalorieManager calorieManager) { super(compositeCountryManager, calorieManager); historicalConsumptionReader = new StringTabularReader(",", new String[]{"Year", "Country", "Item", "population", "cpc"}); historicalConsumptionReader.read(ModelConfig.DEMAND_CONSUMPTION_FILE); + + histWoodConsumpReader = new StringTabularReader(",", new String[]{"Year", "Country", "Item", "Demand"}); + histWoodConsumpReader.read(ModelConfig.HIST_WOOD_CONSUMP_FILE); } @Override @@ -56,7 +60,24 @@ public class DemandManagerFromFile extends AbstractDemandManager { @Override protected Map<WoodType, Double> getWoodDemand(SingleCountry country, int year) { - // TODO Auto-generated method stub - return null; + Map<WoodType, Double> woodDemandMap = new HashMap<WoodType, Double>(); + + for (WoodType woodType : WoodType.values()) { + Map<String, String> queryMap = new HashMap<String, String>(); + queryMap.put("Year", Integer.toString(year)); + queryMap.put("Country", country.getCountryName()); + queryMap.put("Item", woodType.getName()); + try { + Map<String, String> row = histWoodConsumpReader.querySingle(queryMap); + String demand = row.get("Demand"); + double d = Double.valueOf(demand) * ModelConfig.WOOD_BIOMASS_CONVERSION_FACTOR; + woodDemandMap.put(woodType, d); + } + catch (Exception e) { + LogWriter.println("Problem finding wood demand: " + woodType.getName() + ", " + year + ", " + country.getCountryName()); + } + } + + return woodDemandMap; } } \ No newline at end of file diff --git a/src/ac/ed/lurg/demand/WoodDemandManager.java b/src/ac/ed/lurg/demand/WoodDemandManager.java index 633673a0e3f68268e7123e586098688dff539410..35d237af92501c72931ee0eb1d64e5def3092756 100644 --- a/src/ac/ed/lurg/demand/WoodDemandManager.java +++ b/src/ac/ed/lurg/demand/WoodDemandManager.java @@ -14,8 +14,9 @@ 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 int YEAR_COL = 1; + private static final int WOOD_TYPE_COL = 2; + private static final int DEMAND_COL = 3; private Map<SingleCountry, Map<WoodType, Double>> baseWoodDemand = new HashMap<SingleCountry, Map<WoodType, Double>>(); @@ -25,23 +26,30 @@ public class WoodDemandManager { public void readWoodDemandData() { - String filename = ModelConfig.TIMBER_DEMAND_FILE; + String filename = ModelConfig.WOOD_DEMAND_FILE; try { BufferedReader reader = new BufferedReader(new FileReader(filename)); String line, countryName, woodTypeName; Double demand; + Integer year; reader.readLine(); // read header while ((line=reader.readLine()) != null) { String[] tokens = line.split(","); - if (tokens.length < 3) + if (tokens.length < 6) LogWriter.printlnError("Too few columns in " + filename + ", " + line); + year = Integer.valueOf(tokens[YEAR_COL]); + + if (year != ModelConfig.BASE_YEAR) + continue; + 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); diff --git a/src/ac/ed/lurg/forestry/WoodYieldRasterSet.java b/src/ac/ed/lurg/forestry/WoodYieldRasterSet.java index 995c88f2945b5d2049b714844ebbb3aade2772df..c9853457b0e6a63cef469e870fcd6f86bb5a44e2 100644 --- a/src/ac/ed/lurg/forestry/WoodYieldRasterSet.java +++ b/src/ac/ed/lurg/forestry/WoodYieldRasterSet.java @@ -1,7 +1,11 @@ package ac.ed.lurg.forestry; import java.util.Collection; +import java.util.Map; +import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; import ac.sac.raster.RasterHeaderDetails; import ac.sac.raster.RasterKey; import ac.sac.raster.RasterSet; @@ -24,4 +28,20 @@ public class WoodYieldRasterSet extends RasterSet<WoodYieldItem> { popSubsetForKeys(subsetForestGrowthRaster, keys); return subsetForestGrowthRaster; } + + public static double getMaxWoodHarvest(RasterSet<WoodYieldItem> woodYields, RasterSet<LandUseItem> landUses) { + double total = 0; + for (Map.Entry<RasterKey, WoodYieldItem> entry : woodYields.entrySet()) { + RasterKey key = entry.getKey(); + WoodYieldItem wyItem = entry.getValue(); + LandUseItem luItem = landUses.get(key); + if (wyItem == null) + continue; + double area = luItem.getLandCoverArea(LandCoverType.TIMBER_FOREST, LandProtectionType.CONVERTIBLE) + + luItem.getLandCoverArea(LandCoverType.NATURAL, LandProtectionType.CONVERTIBLE); + double rotaYield = wyItem.getYieldAtRotation() / wyItem.getOptimalRotation(); + total += area * rotaYield; + } + return total; + } } diff --git a/src/ac/ed/lurg/landuse/ConversionCostReader.java b/src/ac/ed/lurg/landuse/ConversionCostReader.java index bc87eeaffa2a7b4e5083bcd9385037548de14e14..4648601b1b5cc06d598508c0efcefaf7b00e4361 100644 --- a/src/ac/ed/lurg/landuse/ConversionCostReader.java +++ b/src/ac/ed/lurg/landuse/ConversionCostReader.java @@ -64,18 +64,18 @@ public class ConversionCostReader { conversionCosts.put(new LccKey(LandCoverType.PASTURE, LandCoverType.TIMBER_FOREST), ModelConfig.PASTURE_DECREASE_COST + ModelConfig.MANAGED_FOREST_INCREASE_COST); conversionCosts.put(new LccKey(LandCoverType.PASTURE, LandCoverType.CARBON_FOREST), ModelConfig.PASTURE_DECREASE_COST + ModelConfig.MANAGED_FOREST_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.NATURAL, LandCoverType.CROPLAND), ModelConfig.AGRI_EXPANSION_COST_BASE + ModelConfig.CROP_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.NATURAL, LandCoverType.PASTURE), ModelConfig.AGRI_EXPANSION_COST_BASE + ModelConfig.PASTURE_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.NATURAL, LandCoverType.CROPLAND), ModelConfig.CROP_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.NATURAL, LandCoverType.PASTURE), ModelConfig.PASTURE_INCREASE_COST); conversionCosts.put(new LccKey(LandCoverType.NATURAL, LandCoverType.TIMBER_FOREST), ModelConfig.MANAGED_FOREST_INCREASE_COST); conversionCosts.put(new LccKey(LandCoverType.NATURAL, LandCoverType.CARBON_FOREST), ModelConfig.MANAGED_FOREST_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.CROPLAND), ModelConfig.AGRI_EXPANSION_COST_BASE + ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.CROP_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.PASTURE), ModelConfig.AGRI_EXPANSION_COST_BASE + ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.PASTURE_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.CROPLAND), ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.CROP_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.PASTURE), ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.PASTURE_INCREASE_COST); conversionCosts.put(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.NATURAL), ModelConfig.MANAGED_FOREST_DECREASE_COST); conversionCosts.put(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.CARBON_FOREST), ModelConfig.MANAGED_FOREST_INCREASE_COST + ModelConfig.MANAGED_FOREST_DECREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.CROPLAND), ModelConfig.AGRI_EXPANSION_COST_BASE + ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.CROP_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.PASTURE), ModelConfig.AGRI_EXPANSION_COST_BASE + ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.PASTURE_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.CROPLAND), ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.CROP_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.PASTURE), ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.PASTURE_INCREASE_COST); conversionCosts.put(new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.NATURAL), ModelConfig.MANAGED_FOREST_DECREASE_COST); conversionCosts.put(new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.TIMBER_FOREST), ModelConfig.MANAGED_FOREST_INCREASE_COST + ModelConfig.MANAGED_FOREST_DECREASE_COST); diff --git a/src/ac/ed/lurg/landuse/WoodUsageReader.java b/src/ac/ed/lurg/landuse/WoodUsageReader.java index add6e10380049ef7ee38bfbad0e1c6b531c684cd..efc978947b35d46552218ace27e2815aeb808206 100644 --- a/src/ac/ed/lurg/landuse/WoodUsageReader.java +++ b/src/ac/ed/lurg/landuse/WoodUsageReader.java @@ -17,9 +17,10 @@ import ac.ed.lurg.utils.LogWriter; public class WoodUsageReader { private static final int COUNTRY_COL = 0; - private static final int WOOD_TYPE_COL = 1; - private static final int HARVEST_COL = 2; - private static final int NET_IMPORT_COL = 3; + private static final int YEAR_COL = 1; + private static final int WOOD_TYPE_COL = 2; + private static final int NET_IMPORT_COL = 4; + private static final int HARVEST_COL = 5; private CompositeCountryManager compositeCountryManager; @@ -38,19 +39,25 @@ public class WoodUsageReader { } }; - String filename = ModelConfig.WOOD_NET_IMPORTS_FILE; + String filename = ModelConfig.WOOD_DEMAND_FILE; try { BufferedReader reader = new BufferedReader(new FileReader(filename)); String line, countryName, woodTypeName; + Integer year; double harvest, netImport; reader.readLine(); // read header while ((line = reader.readLine()) != null) { String[] tokens = line.split(","); - if (tokens.length < 4) + if (tokens.length < 6) LogWriter.printlnError("Too few columns in file: " + filename + ", line: " + line); + year = Integer.valueOf(tokens[YEAR_COL]); + + if (year != ModelConfig.BASE_YEAR) + continue; + countryName = tokens[COUNTRY_COL]; woodTypeName = tokens[WOOD_TYPE_COL]; harvest = ModelConfig.IS_FORESTRY_ON ? Double.parseDouble(tokens[HARVEST_COL]) * ModelConfig.WOOD_BIOMASS_CONVERSION_FACTOR : 0; // m3 to MtC-eq