diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index b6fa94121b34c0bf7c00657a39228c66be6b376a..eb9064de580b10741c1ebf142a1ceaf87349c00b 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -1,369 +1,364 @@ - - SET all_types / monogastrics, ruminants, cereals, oilcropspulses, wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops, pasture, setaside/; - - SET crop(all_types) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops, pasture, setaside /; - SET crop_less_pasture(crop) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops, setaside/; - SET cereal_crop(crop) / wheat, maize, rice /; - SET oilpulse_crop(crop) / oilcrops, pulses /; - SET feed_crop(crop) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, pasture/; - SET feed_crop_less_pasture(feed_crop) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg /; - SET import_crop(all_types) / monogastrics, ruminants, wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops/; - SET market_crop(crop) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops/; - - SET land_cover / cropland, pasture, timberForest, carbonForest, natural /; - SET forested(land_cover) / timberForest, carbonForest, natural /; - SET managed_forest(land_cover) / timberForest, carbonForest /; - SET non_timber_forest(land_cover) / cropland, pasture, carbonForest, natural /; - SET exc_managed_forest(land_cover)/ cropland, pasture, natural /; - SET exc_natural(land_cover) / cropland, pasture, timberForest, carbonForest /; - ALIAS (land_cover, land_cover_before); - ALIAS (land_cover, land_cover_after); - - SET location; - PARAMETER suitableLandArea(location) areas of land in Mha; - PARAMETER previousCropArea(crop, location) areas for previous timestep in Mha; - PARAMETER previousFertIntensity(crop, location); - PARAMETER previousIrrigIntensity(crop, location); - PARAMETER previousOtherIntensity(crop, location); - PARAMETER previousRuminantFeed(crop); - PARAMETER previousMonogastricFeed(crop); - PARAMETER previousImportAmount(all_types); - PARAMETER previousExportAmount(all_types); - PARAMETER yieldNone(crop, location) yield in t per ha; - PARAMETER yieldFertOnly(crop, location) yield in t per ha; - PARAMETER yieldIrrigOnly(crop, location) yield in t per ha; - PARAMETER yieldBoth(crop, location) yield in t per ha; - PARAMETER yieldShock(crop, location) rate of yield shock; - PARAMETER fertParam(crop, location) yield response to fertilizer parameter; - PARAMETER irrigParam(crop, location) yield response to irrigation parameter; - PARAMETER demand(all_types) in t; - PARAMETER exportPrices(all_types) prices for exports; - PARAMETER importPrices(all_types) prices for imports; - PARAMETER maxNetImport(import_crop) maximum net import for each crop based on world market; - PARAMETER minNetImport(import_crop) minimum net import for each crop based on world market; - PARAMETER irrigCost(location) irrigation cost in cost per 1000 Mlitre or Mha for each litre per m2; - PARAMETER irrigMaxRate(crop, location) max water application rate irrigation in litre per m2; - PARAMETER irrigConstraint(location) max water available for irrigation in litre per m2; - PARAMETER minDemandPerCereal(cereal_crop) min demand for each cereal crop as factor of all cereals; - PARAMETER minDemandPerOilcrop(oilpulse_crop) min demand for oilcrop pulses as factor of total; - PARAMETER seedAndWasteRate(all_types) rate of use for seed and waste combined; - PARAMETER subsidyRate(crop) rates of subsidy compared to costs; - - PARAMETER previousLandCoverArea(land_cover, location) land cover area in Mha; - - PARAMETER carbonFluxRate(land_cover_before, land_cover_after, location) carbon flux - MtC-eq per Mha; - PARAMETER carbonFluxRateNEE(land_cover, location); - PARAMETER woodYieldRota(location); - PARAMETER woodYieldLUC(land_cover_before, land_cover_after, location); - - PARAMETER conversionCost(land_cover, land_cover) cost of converting from one land cover to another; - PARAMETER infrastructureCost(location); - - SCALAR meatEfficency efficiency of converting feed and pasture into animal products; - SCALAR fertiliserUnitCost fert cost at max fert rate; - SCALAR otherIParam yield response to other intensity; - SCALAR otherICost cost of other intensity; - SCALAR unhandledCropRate rate of fruit veg and other crops not modelled; - SCALAR setAsideRate rate of set aside and failed crop; - 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 woodExportPrice price of wood export - $1000 per tC-eq; - SCALAR woodImportPrice price of wood import - $1000 per tC-eq; - PARAMETER forestRotationPeriod(location); - SCALAR woodMaxNetImport; - SCALAR woodMinNetImport; - SCALAR forestEstablishmentCost; - SCALAR vegClearingCostRate cost of clearing vegetation $1000 per tC; - -*$gdxin "C:\Users\Bart\Documents\PhD\GAMS testing area\_gams_java_gdb1.gdx" -$gdxin %gdxincname% -$load location, suitableLandArea, demand -$load previousCropArea, previousFertIntensity, previousIrrigIntensity, previousOtherIntensity, previousRuminantFeed, previousMonogastricFeed, previousImportAmount, previousExportAmount -$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, carbonPrice, woodExportPrice, woodImportPrice -$load conversionCost, woodYieldRota, forestRotationPeriod, woodMaxNetImport, woodMinNetImport, woodDemand -$load forestEstablishmentCost, vegClearingCostRate, carbonFluxRateNEE, woodYieldLUC -$gdxin - - SCALAR delta "use to smooth power function see 7.5 www.gams.com dd docs solversconopt.pdf" / 0.00000000001 /; - - demand(cereal_crop) = demand('cereals') * minDemandPerCereal(cereal_crop); - demand(oilpulse_crop) = demand('oilcropspulses') * minDemandPerOilcrop(oilpulse_crop); - - previousCropArea(crop_less_pasture, location) = previousCropArea(crop_less_pasture, location) * (1.0 - unhandledCropRate); - - - PARAMETER maxExport(import_crop); - PARAMETER minExport(import_crop); - - PARAMETER cropDM(crop) kg DM per kg of feed the conversion from feed to meat is done in the R animal product index. Pasture is in DM terms already - / wheat 0.87 - maize 0.86 - rice 0.89 - oilcrops 0.96 - pulses 0.31 - starchyRoots 0.25 - fruitveg 0.1 - sugar 1 - pasture 1 / ; - - PARAMETER prodCost(crop) cost per ha before intensity values not including fertiliser or irrigation i.e. seed spray and other. In 1000 $ per ha - / wheat 0.32 - maize 0.31 - rice 0.36 - oilcrops 0.2 - pulses 0.31 - starchyRoots 3.14 - fruitveg 4.0 - sugar 3.0 - energyCrops 0.34 / ; - - PARAMETER baseCost(crop); - PARAMETER otherIntCost(crop); - baseCost(crop) = prodCost(crop)*0.3; - otherIntCost(crop) = baseCost(crop)*0.7 + otherICost; - baseCost('pasture') = 0.04; - otherIntCost('pasture') = 0.8 + otherICost; - - maxNetImport(import_crop) = min(maxNetImport(import_crop), demand(import_crop)); - minNetImport(import_crop) = min(minNetImport(import_crop), demand(import_crop)); - - VARIABLES - cropArea(crop, location) total area for crops - Mha - fertI(crop, location) fertilizer intensity for each crop - factor between 0 and 1 - irrigI(crop, location) irrigation intensity for each crop - factor between 0 and 1 - otherIntensity(crop, location) - ruminantFeed(crop) amount of feed for ruminant animals - Mt - monogastricFeed(crop) amount of feed for monogatric animals - Mt - importAmount(all_types) imports of crops and meat - Mt - exportAmount(all_types) exports of crops and meat - Mt - yield(crop, location) yield per area for each crop - t per ha - unitCost(crop, location) cost per area for each crop - cost - totalFeedDM total feed dry matter - landCoverArea(land_cover, location) land cover area in Mha - landCoverChange(land_cover_before, land_cover_after, location) land cover change in Mha - totalConversionCost(location) land cover conversion cost - $1000 per ha - woodExported total wood sold - Mt C-eq - woodImported - woodSupply - woodProdCost - vegClearningCost(location) - carbonFlux(location) total carbon flux - Mt C - supply(all_types) -* 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, woodHarvestNat, woodHarvestLuc, woodExported, woodImported, - supply, woodSold, woodSupply, exportAmount, forestRotaHarvest, woodProdCost, vegClearningCost; - -* POSITIVE VARIABLE A; - - EQUATIONS - UNIT_COST_EQ(crop, location) cost per area - $1000 per ha or $billion per Mha - YIELD_EQ(crop, location) yield given chosen intensity - tonnes per hectare - MAX_FERT_INTENSITY_CONSTRAINT(crop, location) constraint on maximum fertilizer intensity - MAX_IRRIG_INTENSITY_CONSTRAINT(crop, location) constraint on maximum irrigation intensity - MAX_OTHER_INTENSITY_CONSTRAINT(crop, location) - IRRIGATION_CONSTRAINT(location) constraint on water usage - - SUPPLY_CALC(crop) - RUMINANTS_SUPPLY_CALC - MONOGASTRICS_SUPPLY_CALC - TOTAL_NON_PASTURE_FEED_DM_CALC calc total feed dry matter not including pasture - FEED_MIX_CONSTRAINT(feed_crop_less_pasture) limit amount of feed for each feed crop - SUPPLY_CONSTRAINT(import_crop) - - MAX_NET_IMPORT_CONSTRAINT(import_crop) constraint on max net imports - MIN_NET_IMPORT_CONSTRAINT(import_crop) constraint on min net imports - PASTURE_IMPORT_CONSTRAINT constraint to not import pasture - PASTURE_EXPORT_CONSTRAINT - - - SETASIDE_AREA_CALC(location) - CROPLAND_LAND_COVER_CALC(location) cropland area equals sum of all crop areas - PASTURE_LAND_COVER_CALC(location) pasture area (land cover) equals pasture area (land use) - LAND_COVER_CHANGE_CALC(land_cover, location) "calc land cover change. landCoverChange(A, A, location) defined as unchanged land cover" - LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) conservation of land area - CONVERSION_COST(location) cost of land cover conversion - - WOOD_SUPPLY_CALC - WOOD_SUPPLY_CONSTRAINT - WOOD_MIN_TRADE_CONSTRAINT constraint on minimum wood export - WOOD_MAX_TRADE_CONSTRAINT - WOOD_PROD_COST_CALC - VEG_CLEARING_COST_CALC(location) - - CARBON_FLUX_CALC(location) calc carbon flux - - COST_CALC Net Present Value function; - -**************** Crop cost ************************** - - UNIT_COST_EQ(crop, location) .. unitCost(crop, location) =E= ( baseCost(crop) + - fertiliserUnitCost * fertI(crop, location) + - irrigCost(location) * irrigMaxRate(crop, location) * irrigI(crop, location) + - otherIntCost(crop) * otherIntensity(crop, location) - ); - -**************** Crop yields ******************* - - YIELD_EQ(crop, location) .. yield(crop, location) =E= ( - yieldNone(crop, location) + - (yieldFertOnly(crop, location) - yieldNone(crop, location)) * (1 - exp(-fertParam(crop, location)*fertI(crop, location))) + - (yieldIrrigOnly(crop, location) - yieldNone(crop, location)) * (1 - exp(-irrigParam(crop, location)*irrigI(crop, location))) + - (yieldBoth(crop, location) + yieldNone(crop, location) - yieldFertOnly(crop, location) - yieldIrrigOnly(crop, location)) * - (1 - exp(-fertParam(crop, location)*fertI(crop, location))) * (1 - exp(-irrigParam(crop, location)*irrigI(crop, location))) - ) * (1 - exp(-otherIntensity(crop, location)*otherIParam)); - - MAX_FERT_INTENSITY_CONSTRAINT(crop, location) .. fertI(crop, location) =L= 1; - MAX_IRRIG_INTENSITY_CONSTRAINT(crop, location) .. irrigI(crop, location) =L= 1; - MAX_OTHER_INTENSITY_CONSTRAINT(crop, location) .. otherIntensity(crop, location) =L= 1; - - IRRIGATION_CONSTRAINT(location) .. irrigConstraint(location) * suitableLandArea(location) * (1.0 - unhandledCropRate) =G= sum(crop, irrigMaxRate(crop, location) * irrigI(crop, location) * cropArea(crop, location)); - -*************** Commodity supply ************************* - - SUPPLY_CALC(crop) .. supply(crop) =E= sum(location, cropArea(crop, location) * yield(crop, location)) * (1 - seedAndWasteRate(crop)) - ruminantFeed(crop) - monogastricFeed(crop); - - RUMINANTS_SUPPLY_CALC .. supply('ruminants') =E= meatEfficency * sum(feed_crop, ruminantFeed(feed_crop) * cropDM(feed_crop)) * (1 - seedAndWasteRate('ruminants')); - - MONOGASTRICS_SUPPLY_CALC .. supply('monogastrics') =E= meatEfficency * sum(feed_crop_less_pasture, monogastricFeed(feed_crop_less_pasture) * cropDM(feed_crop_less_pasture)) * (1 - seedAndWasteRate('monogastrics')); - - TOTAL_NON_PASTURE_FEED_DM_CALC .. totalFeedDM =E= sum(feed_crop_less_pasture, (ruminantFeed(feed_crop_less_pasture) + monogastricFeed(feed_crop_less_pasture)) * cropDM(feed_crop_less_pasture)); - FEED_MIX_CONSTRAINT(feed_crop_less_pasture) .. (ruminantFeed(feed_crop_less_pasture) + monogastricFeed(feed_crop_less_pasture)) * cropDM(feed_crop_less_pasture) =L= totalFeedDM * 0.7; - - SUPPLY_CONSTRAINT(import_crop) .. supply(import_crop) =E= demand(import_crop) + exportAmount(import_crop) - importAmount(import_crop); - -*************** Exports ******************************* - - MAX_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =L= maxNetImport(import_crop); - MIN_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =G= minNetImport(import_crop); - PASTURE_IMPORT_CONSTRAINT .. importAmount('pasture') =E= 0; - PASTURE_EXPORT_CONSTRAINT .. exportAmount('pasture') =E= 0; - -************** Land Cover ***************************** - - SETASIDE_AREA_CALC(location) .. cropArea('setaside', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) * setAsideRate; - - CROPLAND_LAND_COVER_CALC(location) .. landCoverArea('cropland', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) / (1.0 - unhandledCropRate); - - PASTURE_LAND_COVER_CALC(location) .. landCoverArea('pasture', location) =E= cropArea('pasture', location); - - LAND_COVER_CHANGE_CALC(land_cover, location) .. landCoverArea(land_cover, location) =E= previousLandCoverArea(land_cover, location) + - sum(land_cover_before, landCoverChange(land_cover_before, land_cover, location)) - - sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)); - - - LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) .. sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)) =E= previousLandCoverArea(land_cover, location); - - 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)); - -************* Forestry *********************************** - - WOOD_SUPPLY_CALC .. woodSupply =E= sum(location, landCoverArea('timberForest', location) * woodYieldRota(location) / forestRotationPeriod(location)); - - WOOD_SUPPLY_CONSTRAINT .. woodSupply =E= woodDemand + woodExported - woodImported; - - WOOD_MIN_TRADE_CONSTRAINT .. woodImported - woodExported =L= woodMaxNetImport; - - WOOD_MAX_TRADE_CONSTRAINT .. woodImported - woodExported =G= woodMinNetImport; - - WOOD_PROD_COST_CALC .. woodProdCost =E= sum(location, landCoverArea('timberForest', location) * forestEstablishmentCost / forestRotationPeriod(location)); - - VEG_CLEARING_COST_CALC(location) .. vegClearningCost(location) =E= sum((land_cover_before, land_cover_after), - landCoverChange(land_cover_before, land_cover_after, location) * woodYieldLUC(land_cover_before, land_cover_after, location)) * vegClearingCostRate; - -*********** Carbon fluxes *********************************** - - 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)) + - sum(land_cover, landCoverArea(land_cover, location) * carbonFluxRateNEE(land_cover, location)); - -************ Net Present Value ****************************** - - COST_CALC .. total_cost =E= sum((crop, location), cropArea(crop, location) * unitCost(crop, location) * (1-subsidyRate(crop))) * domesticPriceMarkup + - sum(location, totalConversionCost(location)) + - sum(import_crop, importPrices(import_crop) * importAmount(import_crop)) + - woodImported * woodImportPrice - - sum(import_crop, exportPrices(import_crop) * exportAmount(import_crop)) - - woodExported * woodExportPrice + - woodProdCost + - sum(location, vegClearningCost(location)); - - - - - MODEL LAND_USE /ALL/ ; - fertI.L(crop, location) = previousFertIntensity(crop, location); - irrigI.L(crop, location) = previousIrrigIntensity(crop, location); - otherIntensity.L(crop, location) = previousOtherIntensity(crop, location); - cropArea.L(crop, location) = previousCropArea(crop, location); - landCoverArea.L(land_cover, location) = previousLandCoverArea(land_cover, location); - landCoverChange.L(land_cover, land_cover, location) = previousLandCoverArea(land_cover, location); - ruminantFeed.L(feed_crop) = previousRuminantFeed(feed_crop); - monogastricFeed.L(feed_crop) = previousMonogastricFeed(feed_crop); - importAmount.L(all_types) = previousImportAmount(all_types); - exportAmount.L(all_types) = previousExportAmount(all_types); - -* LAND_USE.OptFile = 1; - -* display landCoverChange.L - - SOLVE LAND_USE USING NLP MINIMIZING total_cost; -* SOLVE LAND_USE USING NLP MAXIMIZING npv; - - display previousCropArea, irrigMaxRate, otherIntensity.L, fertI.L, irrigI.L, cropArea.L; - display supply.l, demand, ruminantFeed.l, monogastricFeed.l, exportAmount.l; - -* Calculate summary information used in Java process - parameter totalProd(all_types); - parameter totalProdCost(all_types); - parameter totalArea(crop); - parameter totalCropland(location); - parameter netImportAmount(all_types); - parameter netImportCost(all_types); - 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)); - productionShock(crop) = sum(location, cropArea.l(crop, location) * yield.l(crop, location) * yieldShock(crop, location)); - totalProd('ruminants') = meatEfficency*(sum(feed_crop, ruminantFeed.l(feed_crop) * cropDM(feed_crop))); - totalProd('monogastrics') = meatEfficency*(sum(feed_crop, monogastricFeed.l(feed_crop) * cropDM(feed_crop))); - -* Cost based on adjusted area - cropArea.l(crop_less_pasture, location) = cropArea.l(crop_less_pasture, location) / (1.0 - unhandledCropRate); - totalProdCost(crop) = sum(location, unitCost.l(crop, location) * cropArea.l(crop, location)); - totalCropland(location) = sum(crop_less_pasture, cropArea.l(crop_less_pasture, location)); - totalArea(crop) = sum(location, cropArea.l(crop, location)); - - feedCostRate(feed_crop)$[totalProd(feed_crop) <> 0] = totalProdCost(feed_crop) / totalProd(feed_crop); - totalProdCost('ruminants') = sum(feed_crop, ruminantFeed.l(feed_crop) * feedCostRate(feed_crop)); - totalProdCost('monogastrics') = sum(feed_crop, monogastricFeed.l(feed_crop) * feedCostRate(feed_crop)); - - netImportAmount(import_crop) = importAmount.l(import_crop) - exportAmount.l(import_crop); - netImportCost(import_crop) = importAmount.l(import_crop) * importPrices(import_crop) - exportAmount.l(import_crop) * exportPrices(import_crop); - - - netCarbonFlux = SUM(location, carbonFlux.L(location)); - netWoodImport = woodImported.L - woodExported.L; -* netWoodImport = 0; - totalWoodHarvest = woodSupply.L; - - Scalar totalCostsLessLU; - - totalCostsLessLU = sum(crop,totalProdCost(crop))+ sum(import_crop,netImportCost(import_crop)); - - - Scalar ms 'model status', ss 'solve status'; - ms=LAND_USE.modelstat; - ss=LAND_USE.solvestat; + + SET all_types / monogastrics, ruminants, cereals, oilcropspulses, wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops, pasture, setaside/; + + SET crop(all_types) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops, pasture, setaside /; + SET crop_less_pasture(crop) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops, setaside/; + SET cereal_crop(crop) / wheat, maize, rice /; + SET oilpulse_crop(crop) / oilcrops, pulses /; + SET feed_crop(crop) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, pasture/; + SET feed_crop_less_pasture(feed_crop) / wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg /; + SET import_crop(all_types) / monogastrics, ruminants, wheat, maize, rice, oilcrops, pulses, starchyRoots, fruitveg, sugar, energycrops/; + + SET land_cover / cropland, pasture, timberForest, carbonForest, natural /; + SET managed_forest(land_cover) / timberForest, carbonForest /; + ALIAS (land_cover, land_cover_before); + ALIAS (land_cover, land_cover_after); + + SET location; + PARAMETER suitableLandArea(location) areas of land in Mha; + PARAMETER previousCropArea(crop, location) areas for previous timestep in Mha; + PARAMETER previousFertIntensity(crop, location); + PARAMETER previousIrrigIntensity(crop, location); + PARAMETER previousOtherIntensity(crop, location); + PARAMETER previousRuminantFeed(crop); + PARAMETER previousMonogastricFeed(crop); + PARAMETER previousImportAmount(all_types); + PARAMETER previousExportAmount(all_types); + PARAMETER yieldNone(crop, location) yield in t per ha; + PARAMETER yieldFertOnly(crop, location) yield in t per ha; + PARAMETER yieldIrrigOnly(crop, location) yield in t per ha; + PARAMETER yieldBoth(crop, location) yield in t per ha; + PARAMETER yieldShock(crop, location) rate of yield shock; + PARAMETER fertParam(crop, location) yield response to fertilizer parameter; + PARAMETER irrigParam(crop, location) yield response to irrigation parameter; + PARAMETER demand(all_types) in t; + PARAMETER exportPrices(all_types) prices for exports; + PARAMETER importPrices(all_types) prices for imports; + PARAMETER maxNetImport(import_crop) maximum net import for each crop based on world market; + PARAMETER minNetImport(import_crop) minimum net import for each crop based on world market; + PARAMETER irrigCost(location) irrigation cost in cost per 1000 Mlitre or Mha for each litre per m2; + PARAMETER irrigMaxRate(crop, location) max water application rate irrigation in litre per m2; + PARAMETER irrigConstraint(location) max water available for irrigation in litre per m2; + PARAMETER minDemandPerCereal(cereal_crop) min demand for each cereal crop as factor of all cereals; + PARAMETER minDemandPerOilcrop(oilpulse_crop) min demand for oilcrop pulses as factor of total; + PARAMETER seedAndWasteRate(all_types) rate of use for seed and waste combined; + PARAMETER subsidyRate(crop) rates of subsidy compared to costs; + + PARAMETER previousLandCoverArea(land_cover, location) land cover area in Mha; + PARAMETER carbonFluxRateLUC(land_cover_before, land_cover_after, location) carbon flux from land use change - MtC-eq per Mha; + PARAMETER carbonFluxRateNEE(land_cover, location) net ecosystem exchange carbon flux over carbon horizon; + PARAMETER woodYieldRota(location) wood yield from forest rotation - MtC per Mha; + PARAMETER woodYieldLUC(land_cover_before, land_cover_after, location) wood yield from land cover change; + PARAMETER forestRotationPeriod(location) timber forest rotation period - years; + PARAMETER conversionCost(land_cover, land_cover) cost of converting from one land cover to another; + + SCALAR meatEfficency efficiency of converting feed and pasture into animal products; + SCALAR fertiliserUnitCost fert cost at max fert rate; + SCALAR otherIParam yield response to other intensity; + SCALAR otherICost cost of other intensity; + SCALAR unhandledCropRate rate of fruit veg and other crops not modelled; + SCALAR setAsideRate rate of set aside and failed crop; + SCALAR domesticPriceMarkup factor price increased from cost of production; + SCALAR maxLandExpansionRate max rate of country land expansion; + + SCALAR woodDemand Mt C-eq; + SCALAR woodExportPrice price of wood export - $1000 per tC-eq; + SCALAR woodImportPrice price of wood import - $1000 per tC-eq; + SCALAR woodMaxNetImport; + SCALAR woodMinNetImport; + SCALAR forestEstablishmentCost cast $1000 per hectare; + SCALAR vegClearingCostRate cost of clearing vegetation $1000 per tC; + + SCALAR carbonPrice price of carbon - $1000 per tonne; + +*$gdxin "C:\Users\Bart\Documents\PhD\GAMS testing area\_gams_java_gdb1.gdx" +$gdxin %gdxincname% +$load location, suitableLandArea, demand +$load previousCropArea, previousFertIntensity, previousIrrigIntensity, previousOtherIntensity, previousRuminantFeed, previousMonogastricFeed, previousImportAmount, previousExportAmount +$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, carbonFluxRateLUC, carbonFluxRateNEE, woodYieldRota, woodYieldLUC, forestRotationPeriod, conversionCost +$load woodDemand, woodExportPrice, woodImportPrice, woodMaxNetImport, woodMinNetImport, forestEstablishmentCost, vegClearingCostRate, carbonPrice +$gdxin + + SCALAR delta "use to smooth power function see 7.5 www.gams.com dd docs solversconopt.pdf" / 0.00000000001 /; + + demand(cereal_crop) = demand('cereals') * minDemandPerCereal(cereal_crop); + demand(oilpulse_crop) = demand('oilcropspulses') * minDemandPerOilcrop(oilpulse_crop); + + previousCropArea(crop_less_pasture, location) = previousCropArea(crop_less_pasture, location) * (1.0 - unhandledCropRate); + + PARAMETER cropDM(crop) kg DM per kg of feed the conversion from feed to meat is done in the R animal product index. Pasture is in DM terms already + / wheat 0.87 + maize 0.86 + rice 0.89 + oilcrops 0.96 + pulses 0.31 + starchyRoots 0.25 + fruitveg 0.1 + sugar 1 + pasture 1 / ; + + PARAMETER prodCost(crop) cost per ha before intensity values not including fertiliser or irrigation i.e. seed spray and other. In 1000 $ per ha + / wheat 0.32 + maize 0.31 + rice 0.36 + oilcrops 0.2 + pulses 0.31 + starchyRoots 3.14 + fruitveg 4.0 + sugar 3.0 + energyCrops 0.34 / ; + + PARAMETER baseCost(crop); + PARAMETER otherIntCost(crop); + baseCost(crop) = prodCost(crop)*0.3; + otherIntCost(crop) = baseCost(crop)*0.7 + otherICost; + baseCost('pasture') = 0.04; + otherIntCost('pasture') = 0.8 + otherICost; + +* maxNetImport(import_crop) = min(maxNetImport(import_crop), demand(import_crop)); +* minNetImport(import_crop) = min(minNetImport(import_crop), demand(import_crop)); + + VARIABLES + cropArea(crop, location) total area for crops - Mha + fertI(crop, location) fertilizer intensity for each crop - factor between 0 and 1 + irrigI(crop, location) irrigation intensity for each crop - factor between 0 and 1 + otherIntensity(crop, location) + ruminantFeed(crop) amount of feed for ruminant animals - Mt + monogastricFeed(crop) amount of feed for monogatric animals - Mt + importAmount(all_types) imports of crops and meat - Mt + exportAmount(all_types) exports of crops and meat - Mt + yield(crop, location) yield per area for each crop - t per ha + unitCost(crop, location) cost per area for each crop - cost + totalFeedDM total feed dry matter + supply(all_types) crop supply + landCoverArea(land_cover, location) land cover area in Mha + landCoverChange(land_cover_before, land_cover_after, location) land cover change in Mha + totalConversionCost(location) land cover conversion cost - $1000 per ha + woodExported total wood sold - Mt C-eq + woodImported + woodSupplyRota + woodSupplyLUC + woodProdCost + vegClearningCost + 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, + agriLandExpansion, cropIncrease, cropDecrease, pastureDecrease, pastureIncrease, totalFeedDM, + landCoverArea, landCoverChange, totalConversionCost, woodExported, woodImported, woodSupplyRota, + woodSupplyLUC, woodProdCost, vegClearningCost; + + +* POSITIVE VARIABLE A; + + EQUATIONS + UNIT_COST_EQ(crop, location) cost per area - $1000 per ha or $billion per Mha + YIELD_EQ(crop, location) yield given chosen intensity - tonnes per hectare + MAX_FERT_INTENSITY_CONSTRAINT(crop, location) constraint on maximum fertilizer intensity + MAX_IRRIG_INTENSITY_CONSTRAINT(crop, location) constraint on maximum irrigation intensity + MAX_OTHER_INTENSITY_CONSTRAINT(crop, location) + IRRIGATION_CONSTRAINT(location) constraint on water usage + + SUPPLY_CALC(crop) + RUMINANTS_SUPPLY_CALC + MONOGASTRICS_SUPPLY_CALC + TOTAL_NON_PASTURE_FEED_DM_CALC calc total feed dry matter not including pasture + FEED_MIX_CONSTRAINT(feed_crop_less_pasture) limit amount of feed for each feed crop + SUPPLY_CONSTRAINT(import_crop) + + MAX_NET_IMPORT_CONSTRAINT(import_crop) constraint on max net imports + MIN_NET_IMPORT_CONSTRAINT(import_crop) constraint on min net imports + PASTURE_IMPORT_CONSTRAINT constraint to not import pasture + PASTURE_EXPORT_CONSTRAINT + + + SETASIDE_AREA_CALC(location) + CROPLAND_LAND_COVER_CALC(location) cropland area equals sum of all crop areas + PASTURE_LAND_COVER_CALC(location) pasture area (land cover) equals pasture area (land use) + LAND_COVER_CHANGE_CALC(land_cover, location) "calc land cover change. landCoverChange(A, A, location) defined as unchanged land cover" + LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) conservation of land area + CONVERSION_COST(location) cost of land cover conversion + + WOOD_SUPPLY_ROTA_CALC + WOOD_SUPPLY_LUC_CALC + WOOD_SUPPLY_CONSTRAINT + WOOD_MIN_TRADE_CONSTRAINT constraint on minimum wood export + WOOD_MAX_TRADE_CONSTRAINT + WOOD_PROD_COST_CALC + VEG_CLEARING_COST_CALC + + CARBON_FLUX_CALC(location) calc carbon flux + + COST_CALC Net Present Value function; + +**************** Crop cost ************************** + + UNIT_COST_EQ(crop, location) .. unitCost(crop, location) =E= ( baseCost(crop) + + fertiliserUnitCost * fertI(crop, location) + + irrigCost(location) * irrigMaxRate(crop, location) * irrigI(crop, location) + + otherIntCost(crop) * otherIntensity(crop, location) + ); + +**************** Crop yields ******************* + + YIELD_EQ(crop, location) .. yield(crop, location) =E= ( + yieldNone(crop, location) + + (yieldFertOnly(crop, location) - yieldNone(crop, location)) * (1 - exp(-fertParam(crop, location)*fertI(crop, location))) + + (yieldIrrigOnly(crop, location) - yieldNone(crop, location)) * (1 - exp(-irrigParam(crop, location)*irrigI(crop, location))) + + (yieldBoth(crop, location) + yieldNone(crop, location) - yieldFertOnly(crop, location) - yieldIrrigOnly(crop, location)) * + (1 - exp(-fertParam(crop, location)*fertI(crop, location))) * (1 - exp(-irrigParam(crop, location)*irrigI(crop, location))) + ) * (1 - exp(-otherIntensity(crop, location)*otherIParam)); + + MAX_FERT_INTENSITY_CONSTRAINT(crop, location) .. fertI(crop, location) =L= 1; + MAX_IRRIG_INTENSITY_CONSTRAINT(crop, location) .. irrigI(crop, location) =L= 1; + MAX_OTHER_INTENSITY_CONSTRAINT(crop, location) .. otherIntensity(crop, location) =L= 1; + + IRRIGATION_CONSTRAINT(location) .. irrigConstraint(location) * suitableLandArea(location) * (1.0 - unhandledCropRate) =G= sum(crop, irrigMaxRate(crop, location) * irrigI(crop, location) * cropArea(crop, location)); + +*************** Commodity supply ************************* + + SUPPLY_CALC(crop) .. supply(crop) =E= sum(location, cropArea(crop, location) * yield(crop, location)) * (1 - seedAndWasteRate(crop)) - ruminantFeed(crop) - monogastricFeed(crop); + + RUMINANTS_SUPPLY_CALC .. supply('ruminants') =E= meatEfficency * sum(feed_crop, ruminantFeed(feed_crop) * cropDM(feed_crop)) * (1 - seedAndWasteRate('ruminants')); + + MONOGASTRICS_SUPPLY_CALC .. supply('monogastrics') =E= meatEfficency * sum(feed_crop_less_pasture, monogastricFeed(feed_crop_less_pasture) * cropDM(feed_crop_less_pasture)) * (1 - seedAndWasteRate('monogastrics')); + + TOTAL_NON_PASTURE_FEED_DM_CALC .. totalFeedDM =E= sum(feed_crop_less_pasture, (ruminantFeed(feed_crop_less_pasture) + monogastricFeed(feed_crop_less_pasture)) * cropDM(feed_crop_less_pasture)); + FEED_MIX_CONSTRAINT(feed_crop_less_pasture) .. (ruminantFeed(feed_crop_less_pasture) + monogastricFeed(feed_crop_less_pasture)) * cropDM(feed_crop_less_pasture) =L= totalFeedDM * 0.7; + + SUPPLY_CONSTRAINT(import_crop) .. supply(import_crop) =E= demand(import_crop) + exportAmount(import_crop) - importAmount(import_crop); + +*************** Exports ******************************* + + MAX_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =L= maxNetImport(import_crop); + MIN_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =G= minNetImport(import_crop); + PASTURE_IMPORT_CONSTRAINT .. importAmount('pasture') =E= 0; + PASTURE_EXPORT_CONSTRAINT .. exportAmount('pasture') =E= 0; + +************** Land Cover ***************************** + + SETASIDE_AREA_CALC(location) .. cropArea('setaside', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) * setAsideRate; + + CROPLAND_LAND_COVER_CALC(location) .. landCoverArea('cropland', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) / (1.0 - unhandledCropRate); + + PASTURE_LAND_COVER_CALC(location) .. landCoverArea('pasture', location) =E= cropArea('pasture', location); + + LAND_COVER_CHANGE_CALC(land_cover, location) .. landCoverArea(land_cover, location) =E= previousLandCoverArea(land_cover, location) + + sum(land_cover_before, landCoverChange(land_cover_before, land_cover, location)) - + sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)); + + + LAND_COVER_CHANGE_CONSTRAINT(land_cover, location) .. sum(land_cover_after, landCoverChange(land_cover, land_cover_after, location)) =E= previousLandCoverArea(land_cover, location); + + 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)); + +************* Forestry *********************************** + + WOOD_SUPPLY_ROTA_CALC .. woodSupplyRota =E= sum(location, landCoverArea('timberForest', location) * woodYieldRota(location) / forestRotationPeriod(location)); + + WOOD_SUPPLY_LUC_CALC .. woodSupplyLUC =E= sum((land_cover_before, land_cover_after, location), landCoverChange(land_cover_before, land_cover_after, location) * woodYieldLUC(land_cover_before, land_cover_after, location)); + + WOOD_SUPPLY_CONSTRAINT .. woodSupplyRota =E= woodDemand + woodExported - woodImported; + + WOOD_MIN_TRADE_CONSTRAINT .. woodImported - woodExported =L= woodMaxNetImport; + + WOOD_MAX_TRADE_CONSTRAINT .. woodImported - woodExported =G= woodMinNetImport; + + WOOD_PROD_COST_CALC .. woodProdCost =E= sum(location, landCoverArea('timberForest', location) * forestEstablishmentCost / forestRotationPeriod(location)); + + VEG_CLEARING_COST_CALC .. vegClearningCost =E= woodSupplyLUC * vegClearingCostRate; + +*********** Carbon fluxes *********************************** + + CARBON_FLUX_CALC(location) .. carbonFlux(location) =E= sum((land_cover_before, land_cover_after), + landCoverChange(land_cover_before, land_cover_after, location) * carbonFluxRateLUC(land_cover_before, land_cover_after, location)) + + sum((land_cover_before, land_cover_after)$[not sameAs(land_cover_before, land_cover_after)], + landCoverChange(land_cover_before, land_cover_after, location) * carbonFluxRateNEE(land_cover_after, location)); + +************ Total cost ****************************** + + COST_CALC .. total_cost =E= sum((crop, location), cropArea(crop, location) * unitCost(crop, location) * (1-subsidyRate(crop))) * domesticPriceMarkup + + sum(location, totalConversionCost(location)) + + sum(import_crop, importPrices(import_crop) * importAmount(import_crop)) + + woodImported * woodImportPrice - + sum(import_crop, exportPrices(import_crop) * exportAmount(import_crop)) - + woodExported * woodExportPrice + + woodProdCost + + vegClearningCost + + sum(location, carbonFlux(location)) * carbonPrice; + + + + + MODEL LAND_USE /ALL/ ; + fertI.L(crop, location) = previousFertIntensity(crop, location); + irrigI.L(crop, location) = previousIrrigIntensity(crop, location); + otherIntensity.L(crop, location) = previousOtherIntensity(crop, location); + cropArea.L(crop, location) = previousCropArea(crop, location); + landCoverArea.L(land_cover, location) = previousLandCoverArea(land_cover, location); + landCoverChange.L(land_cover, land_cover, location) = previousLandCoverArea(land_cover, location); + ruminantFeed.L(feed_crop) = previousRuminantFeed(feed_crop); + monogastricFeed.L(feed_crop) = previousMonogastricFeed(feed_crop); + importAmount.L(all_types) = previousImportAmount(all_types); + exportAmount.L(all_types) = previousExportAmount(all_types); + +* LAND_USE.OptFile = 1; + +* display landCoverChange.L + + SOLVE LAND_USE USING NLP MINIMIZING total_cost; + + display previousCropArea, irrigMaxRate, otherIntensity.L, fertI.L, irrigI.L, cropArea.L; + display supply.l, demand, ruminantFeed.l, monogastricFeed.l, exportAmount.l; + +* Calculate summary information used in Java process + parameter totalProd(all_types); + parameter totalProdCost(all_types); + parameter totalArea(crop); + parameter totalCropland(location); + parameter netImportAmount(all_types); + parameter netImportCost(all_types); + 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)); + productionShock(crop) = sum(location, cropArea.l(crop, location) * yield.l(crop, location) * yieldShock(crop, location)); + totalProd('ruminants') = meatEfficency*(sum(feed_crop, ruminantFeed.l(feed_crop) * cropDM(feed_crop))); + totalProd('monogastrics') = meatEfficency*(sum(feed_crop, monogastricFeed.l(feed_crop) * cropDM(feed_crop))); + +* Cost based on adjusted area + cropArea.l(crop_less_pasture, location) = cropArea.l(crop_less_pasture, location) / (1.0 - unhandledCropRate); + totalProdCost(crop) = sum(location, unitCost.l(crop, location) * cropArea.l(crop, location)); + totalCropland(location) = sum(crop_less_pasture, cropArea.l(crop_less_pasture, location)); + totalArea(crop) = sum(location, cropArea.l(crop, location)); + + feedCostRate(feed_crop)$[totalProd(feed_crop) <> 0] = totalProdCost(feed_crop) / totalProd(feed_crop); + totalProdCost('ruminants') = sum(feed_crop, ruminantFeed.l(feed_crop) * feedCostRate(feed_crop)); + totalProdCost('monogastrics') = sum(feed_crop, monogastricFeed.l(feed_crop) * feedCostRate(feed_crop)); + + netImportAmount(import_crop) = importAmount.l(import_crop) - exportAmount.l(import_crop); + netImportCost(import_crop) = importAmount.l(import_crop) * importPrices(import_crop) - exportAmount.l(import_crop) * exportPrices(import_crop); + + + netCarbonFlux = SUM(location, carbonFlux.L(location)); +* including wood from LUC as export + netWoodImport = woodImported.L - woodExported.L; +* netWoodImport = 0; + totalWoodHarvest = woodSupplyRota.L + woodSupplyLUC.L; + + Scalar totalCostsLessLU; + + totalCostsLessLU = sum(crop,totalProdCost(crop))+ sum(import_crop,netImportCost(import_crop)); + + + Scalar ms 'model status', ss 'solve status'; + ms=LAND_USE.modelstat; + ss=LAND_USE.solvestat; diff --git a/src/ac/ed/lurg/InternationalMarket.java b/src/ac/ed/lurg/InternationalMarket.java index 8934b0359eb93aeb50fcdebb6ea8c71e0c63829c..86350e1f3732cd35df42a619771cbfeccfb6e1df 100644 --- a/src/ac/ed/lurg/InternationalMarket.java +++ b/src/ac/ed/lurg/InternationalMarket.java @@ -1,293 +1,295 @@ -package ac.ed.lurg; - -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import ac.ed.lurg.country.AbstractCountryAgent; -import ac.ed.lurg.country.GlobalPrice; -import ac.ed.lurg.country.StockReader; -import ac.ed.lurg.landuse.CropUsageData; -import ac.ed.lurg.landuse.WoodUsageData; -import ac.ed.lurg.shock.PriceShockManager; -import ac.ed.lurg.types.CropToDoubleMap; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.WoodType; -import ac.ed.lurg.utils.LogWriter; - -public class InternationalMarket { - - private Map<CropType, GlobalPrice> worldPrices; - private GlobalPrice carbonPrice; - private GlobalPrice woodPrice; - private PriceShockManager priceShockManager; - - @SuppressWarnings("unchecked") - public InternationalMarket() { - if(ModelConfig.IS_CALIBRATION_RUN) { - worldPrices = new HashMap<CropType, GlobalPrice>(); - - - Map<CropType, Double> stockLevel = getInitialStockLevels(); - - for (CropType crop : CropType.getImportedTypes()) { - Double initialStock = stockLevel.get(crop); - worldPrices.put(crop, GlobalPrice.createInitial(crop.getInitialPrice(), initialStock)); - } - - carbonPrice = GlobalPrice.createInitial(ModelConfig.INIT_CARBON_PRICE, 0); - woodPrice = GlobalPrice.createInitial(ModelConfig.INIT_WOOD_PRICE, ModelConfig.INIT_WOOD_STOCK); - - } - else { - List<Object> deserializedPrices = deserializeGlobalPrice(); - worldPrices = (Map<CropType, GlobalPrice>) deserializedPrices.get(0); - carbonPrice = (GlobalPrice) deserializedPrices.get(1); - woodPrice = (GlobalPrice) deserializedPrices.get(2); - - } - priceShockManager = new PriceShockManager(); - } - - public Map<CropType, GlobalPrice> getWorldPrices() { - return worldPrices; - } - - public GlobalPrice getCarbonPrice() { - if (ModelConfig.IS_CARBON_ON) { - return carbonPrice; - } else { - return carbonPrice.createPriceAdjustedByFactor(0); // set price to 0 but keep export/import amounts for reference - } - } - - public GlobalPrice getWoodPrice() { - if (ModelConfig.IS_FORESTRY_ON) { - return woodPrice; - } else { - return woodPrice.createPriceAdjustedByFactor(0); - } - } - - private Map<CropType, Double> getInitialStockLevels() { - Map<CropType, Double> initialStockLevels = new StockReader().read(); - return initialStockLevels; - } - - void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double carbonDemand, Timestep timestep) { - CropToDoubleMap totalImportCommodities = new CropToDoubleMap(); - CropToDoubleMap totalExportCommodities = new CropToDoubleMap(); - CropToDoubleMap totalProduction = new CropToDoubleMap(); - for (AbstractCountryAgent ca : countryAgents) { - - // Get values for world input costs - Map<CropType, CropUsageData> cropUsage = ca.getCropUsageData(); - - for (Entry<CropType, CropUsageData> entry : cropUsage.entrySet()) { - CropType c = entry.getKey(); - - double countryNetImports = entry.getValue().getShockedNetImports(); - totalProduction.incrementValue(c, (entry.getValue().getProductionExpected()-entry.getValue().getProductionShock())); - - if (countryNetImports > 0) - totalImportCommodities.incrementValue(c, countryNetImports); - else - totalExportCommodities.incrementValue(c, -countryNetImports); - } - } - - // Look at trade balance and adjust appropriately - for (CropType crop : CropType.getImportedTypes()) { - - GlobalPrice prevPrice = worldPrices.get(crop); - double imports = totalImportCommodities.containsKey(crop) ? totalImportCommodities.get(crop) : 0.0; - double exportsBeforeTransportLosses = totalExportCommodities.containsKey(crop) ? totalExportCommodities.get(crop) : 0.0; - LogWriter.println(timestep.getYear() + " Updating " + crop.getGamsName() + " prices"); - GlobalPrice adjustedPrice = prevPrice.createWithUpdatedMarketPrices(imports, exportsBeforeTransportLosses, timestep, totalProduction.get(crop), true); - LogWriter.println( String.format("Price for %s updated from %s to %s \n", crop.getGamsName(), prevPrice, adjustedPrice)); - if (adjustedPrice.getStockLevel() < 0) - LogWriter.println("Global stocks are below zero" + crop.getGamsName() + ", " + timestep.getYear()); - - worldPrices.put(crop, adjustedPrice); - } - - // Update carbon price - double totalCarbonSequestered = 0; - double totalCarbonEmitted = 0; - for (AbstractCountryAgent ca : countryAgents) { - double netCarbonFlux = ca.getNetCarbonFlux(); - if (netCarbonFlux >= 0) - totalCarbonEmitted += netCarbonFlux; - else - totalCarbonSequestered -= netCarbonFlux; - } - - totalCarbonSequestered = Math.max(totalCarbonSequestered, 0.0000001); // avoid division by 0 - GlobalPrice prevCPrice = carbonPrice; - LogWriter.println(timestep.getYear() + " Updating carbon price"); - GlobalPrice adjustedCPrice = prevCPrice.createWithUpdatedMarketPrices(carbonDemand, totalCarbonSequestered, timestep, totalCarbonSequestered, false); - LogWriter.println( String.format("Price for carbon updated from %s to %s \n", prevCPrice, adjustedCPrice)); - if (adjustedCPrice.getStockLevel() < 0) - LogWriter.println("Global stocks are below zero carbon, " + timestep.getYear()); - carbonPrice = adjustedCPrice; - - // Update timber price - double totalWoodImport = 0; - double totalWoodExport = 0; - double totalWoodProduction = 0; - - for (AbstractCountryAgent ca : countryAgents) { - Map<WoodType, WoodUsageData> woodUsageMap = ca.getWoodUsageData(); - for (WoodUsageData woodUsage : woodUsageMap.values()) { - totalWoodProduction += woodUsage.getHarvest(); - double netImport = woodUsage.getNetImport(); - if (netImport >= 0) { - totalWoodImport += netImport; - } else { - totalWoodExport += -netImport; - } - } - } - totalWoodProduction = Math.max(totalWoodProduction, 0.0000001); // avoid division by 0 - GlobalPrice prevTPrice = woodPrice; - 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 wood, " + timestep.getYear()); - woodPrice = adjustedTPrice; - - } - - void writeGlobalMarketFile(Timestep timestep, BufferedWriter outputFile) throws IOException { - for (CropType crop : CropType.getImportedTypes()) { - StringBuffer sbData = new StringBuffer(); - GlobalPrice priceQuantity = worldPrices.get(crop); - sbData.append(String.format("%d,%s", timestep.getYear(), crop.getGamsName())); - sbData.append(String.format(",%.1f,%.1f", priceQuantity.getImportAmount(), priceQuantity.getExportsAfterTransportLosses())); - sbData.append(String.format(",%.8f,%.3f", priceQuantity.getExportPrice(), priceQuantity.getStockLevel())); - - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - // Carbon price - { - StringBuffer sbData = new StringBuffer(); - sbData.append(String.format("%d,%s", timestep.getYear(), "carbon")); - sbData.append(String.format(",%.1f,%.1f", carbonPrice.getImportAmount(), carbonPrice.getExportsAfterTransportLosses())); - sbData.append(String.format(",%.8f,%.3f", carbonPrice.getExportPrice(), carbonPrice.getStockLevel())); - - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - // Timber price - { - StringBuffer sbData = new StringBuffer(); - sbData.append(String.format("%d,%s", timestep.getYear(), "timber")); - sbData.append(String.format(",%.1f,%.1f", woodPrice.getImportAmount(), woodPrice.getExportsAfterTransportLosses())); - sbData.append(String.format(",%.8f,%.3f", woodPrice.getExportPrice(), woodPrice.getStockLevel())); - - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - } - - public void serializeGlobalPrices() { - List<Object> pricesToSerialize = new ArrayList<Object>(); - pricesToSerialize.add(worldPrices); - pricesToSerialize.add(carbonPrice); - pricesToSerialize.add(woodPrice); - - try { - String fileStr = ModelConfig.IS_CALIBRATION_RUN ? ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE : ModelConfig.CHECKPOINT_INTERNATIONAL_MARKET_FILE; - LogWriter.println("Starting serializing GlobalPrice to " + fileStr); - FileOutputStream fileOut = new FileOutputStream(fileStr); - ObjectOutputStream out = new ObjectOutputStream(fileOut); - out.writeObject(pricesToSerialize); - out.close(); - fileOut.close(); - LogWriter.println("Serialized international market data is saved"); - } catch (IOException i) { - i.printStackTrace(); - } - } - - @SuppressWarnings("unchecked") - private List<Object> deserializeGlobalPrice() { - try { - List<Object> initGlobalPrices; - FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE); - ObjectInputStream in = new ObjectInputStream(fileIn); - initGlobalPrices = (List<Object>) in.readObject(); - in.close(); - fileIn.close(); - LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE); - return initGlobalPrices; - } catch (IOException i) { - LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE); - LogWriter.print(i); - return null; - } catch (ClassNotFoundException c) { - LogWriter.printlnError("GlobalPrice class not found"); - c.printStackTrace(); - return null; - } - } - - public void applyPriceShocks(Timestep timestep) { - Map<CropType, Double> shocks = priceShockManager.getShocks(timestep); - for (Map.Entry<CropType, Double> entry : shocks.entrySet()) { - CropType crop = entry.getKey(); - GlobalPrice preShock = worldPrices.get(crop); - double factor = 1.0 + entry.getValue(); - GlobalPrice adjustedPrice = preShock.createPriceAdjustedByFactor(factor); - LogWriter.println(String.format("applyPriceShocks: %s adjusting price by %.4f to export price %.2f", crop.getFaoName(), factor, adjustedPrice.getExportPrice())); - worldPrices.put(crop, adjustedPrice); - } - } - - public boolean negativeStockLevelsExist() { - for (Map.Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) { - double stocklevel = entry.getValue().getStockLevel(); - if (stocklevel < 0) { - LogWriter.println(String.format("negativeStockLevelsExist: %s has negative stock %.3f", entry.getKey().getFaoName(), stocklevel)); - return true; - } - } - LogWriter.println(String.format("No negative stocks found")); - return false; - } - - /* public double findMaxPriceDiff(Map<CropType, Double> previousExportPrices) { - if (previousExportPrices == null) - return Double.MAX_VALUE; - - double maxSoFar = 0; - CropType crop = null; - - for (Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) { - Double previousP = previousExportPrices.get(entry.getKey()); - - if (previousP == null) - return Double.MAX_VALUE; - - double diffThisTime = Math.abs(previousP - entry.getValue().getExportPrice()); - if (diffThisTime > maxSoFar) { - maxSoFar = diffThisTime; - crop = entry.getKey(); - } - } - - LogWriter.println("findMaxPriceDiff: found " + maxSoFar + " for " + crop); - return maxSoFar; - } */ -} +package ac.ed.lurg; + +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import ac.ed.lurg.country.AbstractCountryAgent; +import ac.ed.lurg.country.GlobalPrice; +import ac.ed.lurg.country.StockReader; +import ac.ed.lurg.landuse.CropUsageData; +import ac.ed.lurg.landuse.WoodUsageData; +import ac.ed.lurg.shock.PriceShockManager; +import ac.ed.lurg.types.CropToDoubleMap; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.WoodType; +import ac.ed.lurg.utils.LogWriter; + +public class InternationalMarket { + + private Map<CropType, GlobalPrice> worldPrices; + private GlobalPrice carbonPrice; + private GlobalPrice woodPrice; + private PriceShockManager priceShockManager; + + @SuppressWarnings("unchecked") + public InternationalMarket() { + if(ModelConfig.IS_CALIBRATION_RUN) { + worldPrices = new HashMap<CropType, GlobalPrice>(); + + + Map<CropType, Double> stockLevel = getInitialStockLevels(); + + for (CropType crop : CropType.getImportedTypes()) { + Double initialStock = stockLevel.get(crop); + worldPrices.put(crop, GlobalPrice.createInitial(crop.getInitialPrice(), initialStock)); + } + + carbonPrice = GlobalPrice.createInitial(ModelConfig.INIT_CARBON_PRICE, 0); + woodPrice = GlobalPrice.createInitial(ModelConfig.INIT_WOOD_PRICE, ModelConfig.INIT_WOOD_STOCK); + + } + else { + List<Object> deserializedPrices = deserializeGlobalPrice(); + worldPrices = (Map<CropType, GlobalPrice>) deserializedPrices.get(0); + //carbonPrice = (GlobalPrice) deserializedPrices.get(1); + carbonPrice = GlobalPrice.createInitial(ModelConfig.INIT_CARBON_PRICE, 0); + woodPrice = (GlobalPrice) deserializedPrices.get(2); + + } + priceShockManager = new PriceShockManager(); + } + + public Map<CropType, GlobalPrice> getWorldPrices() { + return worldPrices; + } + + public GlobalPrice getCarbonPrice() { + if (ModelConfig.IS_CARBON_ON) { + return carbonPrice; + } else { + return carbonPrice.createPriceAdjustedByFactor(0); // set price to 0 but keep export/import amounts for reference + } + } + + public GlobalPrice getWoodPrice() { + if (ModelConfig.IS_FORESTRY_ON) { + return woodPrice; + } else { + return woodPrice.createPriceAdjustedByFactor(0); + } + } + + private Map<CropType, Double> getInitialStockLevels() { + Map<CropType, Double> initialStockLevels = new StockReader().read(); + return initialStockLevels; + } + + void determineInternationalTrade(Collection<AbstractCountryAgent> countryAgents, double carbonDemand, Timestep timestep) { + CropToDoubleMap totalImportCommodities = new CropToDoubleMap(); + CropToDoubleMap totalExportCommodities = new CropToDoubleMap(); + CropToDoubleMap totalProduction = new CropToDoubleMap(); + for (AbstractCountryAgent ca : countryAgents) { + + // Get values for world input costs + Map<CropType, CropUsageData> cropUsage = ca.getCropUsageData(); + + for (Entry<CropType, CropUsageData> entry : cropUsage.entrySet()) { + CropType c = entry.getKey(); + + double countryNetImports = entry.getValue().getShockedNetImports(); + totalProduction.incrementValue(c, (entry.getValue().getProductionExpected()-entry.getValue().getProductionShock())); + + if (countryNetImports > 0) + totalImportCommodities.incrementValue(c, countryNetImports); + else + totalExportCommodities.incrementValue(c, -countryNetImports); + } + } + + // Look at trade balance and adjust appropriately + for (CropType crop : CropType.getImportedTypes()) { + + GlobalPrice prevPrice = worldPrices.get(crop); + double imports = totalImportCommodities.containsKey(crop) ? totalImportCommodities.get(crop) : 0.0; + double exportsBeforeTransportLosses = totalExportCommodities.containsKey(crop) ? totalExportCommodities.get(crop) : 0.0; + LogWriter.println(timestep.getYear() + " Updating " + crop.getGamsName() + " prices"); + GlobalPrice adjustedPrice = prevPrice.createWithUpdatedMarketPrices(imports, exportsBeforeTransportLosses, timestep, totalProduction.get(crop), true); + LogWriter.println( String.format("Price for %s updated from %s to %s \n", crop.getGamsName(), prevPrice, adjustedPrice)); + if (adjustedPrice.getStockLevel() < 0) + LogWriter.println("Global stocks are below zero" + crop.getGamsName() + ", " + timestep.getYear()); + + worldPrices.put(crop, adjustedPrice); + } + + // Update carbon price + double totalCarbonSequestered = 0; + double totalCarbonEmitted = 0; + for (AbstractCountryAgent ca : countryAgents) { + double netCarbonFlux = ca.getNetCarbonFlux(); + if (netCarbonFlux >= 0) + totalCarbonEmitted += netCarbonFlux; + else + totalCarbonSequestered -= netCarbonFlux; + } + + totalCarbonSequestered = Math.max(totalCarbonSequestered, 0.0000001); // avoid division by 0 + GlobalPrice prevCPrice = carbonPrice; + LogWriter.println(timestep.getYear() + " Updating carbon price"); + GlobalPrice adjustedCPrice = prevCPrice.createWithUpdatedMarketPrices(carbonDemand, totalCarbonSequestered, timestep, totalCarbonSequestered, false); + LogWriter.println( String.format("Price for carbon updated from %s to %s \n", prevCPrice, adjustedCPrice)); + if (adjustedCPrice.getStockLevel() < 0) + LogWriter.println("Global stocks are below zero carbon, " + timestep.getYear()); + carbonPrice = adjustedCPrice; + + // Update timber price + double totalWoodImport = 0; + double totalWoodExport = 0; + double totalWoodProduction = 0; + + for (AbstractCountryAgent ca : countryAgents) { + Map<WoodType, WoodUsageData> woodUsageMap = ca.getWoodUsageData(); + for (WoodUsageData woodUsage : woodUsageMap.values()) { + totalWoodProduction += woodUsage.getHarvest(); + double netImport = woodUsage.getNetImport(); + double lucHarvest = woodUsage.getLucHarvest(); // assume wood from LUC exported + if (netImport >= 0) { + totalWoodImport += netImport; + } else { + totalWoodExport += -netImport + lucHarvest; + } + } + } + totalWoodProduction = Math.max(totalWoodProduction, 0.0000001); // avoid division by 0 + GlobalPrice prevTPrice = woodPrice; + 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 wood, " + timestep.getYear()); + woodPrice = adjustedTPrice; + + } + + void writeGlobalMarketFile(Timestep timestep, BufferedWriter outputFile) throws IOException { + for (CropType crop : CropType.getImportedTypes()) { + StringBuffer sbData = new StringBuffer(); + GlobalPrice priceQuantity = worldPrices.get(crop); + sbData.append(String.format("%d,%s", timestep.getYear(), crop.getGamsName())); + sbData.append(String.format(",%.1f,%.1f", priceQuantity.getImportAmount(), priceQuantity.getExportsAfterTransportLosses())); + sbData.append(String.format(",%.8f,%.3f", priceQuantity.getExportPrice(), priceQuantity.getStockLevel())); + + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + // Carbon price + { + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%s", timestep.getYear(), "carbon")); + sbData.append(String.format(",%.1f,%.1f", carbonPrice.getImportAmount(), carbonPrice.getExportsAfterTransportLosses())); + sbData.append(String.format(",%.8f,%.3f", carbonPrice.getExportPrice(), carbonPrice.getStockLevel())); + + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + // Timber price + { + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%s", timestep.getYear(), "timber")); + sbData.append(String.format(",%.1f,%.1f", woodPrice.getImportAmount(), woodPrice.getExportsAfterTransportLosses())); + sbData.append(String.format(",%.8f,%.3f", woodPrice.getExportPrice(), woodPrice.getStockLevel())); + + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + } + + public void serializeGlobalPrices() { + List<Object> pricesToSerialize = new ArrayList<Object>(); + pricesToSerialize.add(worldPrices); + pricesToSerialize.add(carbonPrice); + pricesToSerialize.add(woodPrice); + + try { + String fileStr = ModelConfig.IS_CALIBRATION_RUN ? ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE : ModelConfig.CHECKPOINT_INTERNATIONAL_MARKET_FILE; + LogWriter.println("Starting serializing GlobalPrice to " + fileStr); + FileOutputStream fileOut = new FileOutputStream(fileStr); + ObjectOutputStream out = new ObjectOutputStream(fileOut); + out.writeObject(pricesToSerialize); + out.close(); + fileOut.close(); + LogWriter.println("Serialized international market data is saved"); + } catch (IOException i) { + i.printStackTrace(); + } + } + + @SuppressWarnings("unchecked") + private List<Object> deserializeGlobalPrice() { + try { + List<Object> initGlobalPrices; + FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE); + ObjectInputStream in = new ObjectInputStream(fileIn); + initGlobalPrices = (List<Object>) in.readObject(); + in.close(); + fileIn.close(); + LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE); + return initGlobalPrices; + } catch (IOException i) { + LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_INTERNATIONAL_MARKET_FILE); + LogWriter.print(i); + return null; + } catch (ClassNotFoundException c) { + LogWriter.printlnError("GlobalPrice class not found"); + c.printStackTrace(); + return null; + } + } + + public void applyPriceShocks(Timestep timestep) { + Map<CropType, Double> shocks = priceShockManager.getShocks(timestep); + for (Map.Entry<CropType, Double> entry : shocks.entrySet()) { + CropType crop = entry.getKey(); + GlobalPrice preShock = worldPrices.get(crop); + double factor = 1.0 + entry.getValue(); + GlobalPrice adjustedPrice = preShock.createPriceAdjustedByFactor(factor); + LogWriter.println(String.format("applyPriceShocks: %s adjusting price by %.4f to export price %.2f", crop.getFaoName(), factor, adjustedPrice.getExportPrice())); + worldPrices.put(crop, adjustedPrice); + } + } + + public boolean negativeStockLevelsExist() { + for (Map.Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) { + double stocklevel = entry.getValue().getStockLevel(); + if (stocklevel < 0) { + LogWriter.println(String.format("negativeStockLevelsExist: %s has negative stock %.3f", entry.getKey().getFaoName(), stocklevel)); + return true; + } + } + LogWriter.println(String.format("No negative stocks found")); + return false; + } + + /* public double findMaxPriceDiff(Map<CropType, Double> previousExportPrices) { + if (previousExportPrices == null) + return Double.MAX_VALUE; + + double maxSoFar = 0; + CropType crop = null; + + for (Entry<CropType, GlobalPrice> entry : worldPrices.entrySet()) { + Double previousP = previousExportPrices.get(entry.getKey()); + + if (previousP == null) + return Double.MAX_VALUE; + + double diffThisTime = Math.abs(previousP - entry.getValue().getExportPrice()); + if (diffThisTime > maxSoFar) { + maxSoFar = diffThisTime; + crop = entry.getKey(); + } + } + + LogWriter.println("findMaxPriceDiff: found " + maxSoFar + " for " + crop); + return maxSoFar; + } */ +} diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index 69c9cea0015383ec9f061e6ebfab7698b43950c8..8c7d7116f2aa06191c0ab73e5bb8079d29e397ac 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -1,480 +1,480 @@ -package ac.ed.lurg; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Enumeration; -import java.util.Properties; -import java.lang.Long; - -import ac.ed.lurg.shock.parameterShocksReader; -import ac.ed.lurg.types.ModelFitType; -import ac.ed.lurg.types.PriceType; -import ac.ed.lurg.utils.LogWriter; - -public class ModelConfig { - - private Properties configFile; - private static ModelConfig modelConfig; - public static final String CONFIG_FILE = System.getProperty("CONFIG_FILE"); - - private static parameterShocksReader shocksReader; - - private ModelConfig() { - configFile = new Properties(); - try { - System.out.println("Config. file is " + CONFIG_FILE); - if (CONFIG_FILE != null) - configFile.load(new FileInputStream(CONFIG_FILE)); - } - catch (IOException e) { - System.err.println("Problems reading config file"); - System.err.println(e.getMessage()); - } - } - - public static String getSetupDetails() { - String buildVerion = System.getProperty("BUILDVER"); - StringBuffer sb = new StringBuffer("Build version: " + buildVerion + "\n"); - - Properties props = getModelConfig().configFile; - Enumeration<?> em = props.keys(); - while(em.hasMoreElements()) { - String str = (String) em.nextElement(); - sb.append(str + ": " + props.get(str) + "\n"); - } - - return sb.toString(); - } - - private static ModelConfig getModelConfig() { - if (modelConfig == null) - modelConfig = new ModelConfig(); - - return modelConfig; - } - - private static String getProperty(String prop) { - return getModelConfig().getProp(prop); - } - private static String getProperty(String prop, String defaultString) { - String propValue = getProperty(prop); - return propValue == null ? defaultString : propValue; - } - private String getProp(String prop) { - return configFile.getProperty(prop); - } - - private static Integer getIntProperty(String prop, Integer defaultInt) { - Integer propValue = getModelConfig().getIntProp(prop); - return propValue == null ? defaultInt : propValue; - } - private Integer getIntProp(String prop) { - String v = configFile.getProperty(prop); - return v==null ? null : Integer.valueOf(v); - } - - @SuppressWarnings("unused") - private static Long getLongProperty(String prop, Long defaultLong) { - Long propValue = getModelConfig().getLongProp(prop); - return propValue == null ? defaultLong : propValue; - } - private Long getLongProp(String prop) { - String v = configFile.getProperty(prop); - return v==null ? null : Long.valueOf(v); - } - - private static Double getDoubleProperty(String prop, Double defaultDouble) { - Double propValue = getModelConfig().getDoubleProp(prop); - return propValue == null ? defaultDouble : propValue; - } - private Double getDoubleProp(String prop) { - String v = configFile.getProperty(prop); - return v==null ? null : Double.valueOf(v); - } - - private static Boolean getBooleanProperty(String prop, Boolean defaultBoolean) { - return getModelConfig().getBooleanProp(prop, defaultBoolean); - } - private boolean getBooleanProp(String prop, Boolean defaultBoolean) { - String v = configFile.getProperty(prop); - return v==null ? defaultBoolean : Boolean.valueOf(v); - } - - public static double updateParameterForShocks(int year, String parameter) { - - Double value = null; - try { - value = getModelConfig().getClass().getField(parameter).getDouble(parameter); - - if(SHOCKS_POSSIBLE) { - - if (shocksReader == null) { - shocksReader = new parameterShocksReader(","); - shocksReader.read(SHOCKS_PARAMETER_FILE); - } - Double updatedValue = shocksReader.queryForParameter(year, parameter); - - if(updatedValue !=null) - value = updatedValue; - } - - } - catch (IllegalAccessException | NoSuchFieldException e) { - LogWriter.printlnError("cannot find parameter in model config to shock: " + parameter); - LogWriter.print(e); - } - return value; - } - - - public static final boolean SUPPRESS_STD_OUTPUT = getBooleanProperty("SUPPRESS_STD_OUTPUT", Boolean.FALSE); - - // Directory information - public static final String BASE_DIR = getProperty("BASE_DIR"); // this must to be set in config file - public static final String OUTPUT_DIR = getProperty("OUTPUT_DIR", "."); - 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 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"); - - // Country (non-gridded) data - public static final boolean DEMAND_FROM_FILE = getBooleanProperty("DEMAND_FROM_FILE", false); // used in hindcasting - public static final boolean PRICE_ELASTIC_DEMAND = getBooleanProperty("PRICE_ELASTIC_DEMAND", true); - public static final boolean DONT_REBASE_DEMAND = getBooleanProperty("DONT_REBASE_DEMAND", false);; - public static final String DEMAND_CURVES_FILE = getProperty("DEMAND_CURVES_FILE", DATA_DIR + File.separator + "com_curves.csv"); // either DEMAND_CURVES_FILE or DEMAND_CONSUMPTION_FILE is used, but not both - public static final String DEMAND_CONSUMPTION_FILE = getProperty("DEMAND_CONSUMPTION_FILE", DATA_DIR + File.separator + "hist_comsump.csv"); - public static final PriceType PRICE_CALCULATION = PriceType.findByName(getProperty("PRICE_CALCULATION", "weightedImportsExports")); - public static final String SSP_FILENAME = getProperty("SSP_FILENAME", "ssp.csv"); - public static final String SSP_FILE = getProperty("SSP_FILE", DATA_DIR + File.separator + SSP_FILENAME); - public static final String BASELINE_CONSUMP_FILE = DATA_DIR + File.separator + "base_consump.csv"; - public static final String CALORIE_PER_T_FILE = DATA_DIR + File.separator + "calories_per_t.csv"; - public static final String COUNTRY_CODES_FILE = DATA_DIR + File.separator + "country_codes4.csv"; - public static final String COUNTRY_DATA_FILE = DATA_DIR + File.separator + "country_data.csv"; - public static final String NET_IMPORTS_FILE = DATA_DIR + File.separator + "net_imports.csv"; - public static final String BIOENERGY_1GEN_BASE_DEMAND_FILE = DATA_DIR + File.separator + "bio_demand.csv"; - public static final String BIOENERGY_2GEN_DEMAND_FILENAME = getProperty("BIOENERGY_2GEN_DEMAND_FILENAME", "bioenergy_gen2_iiasa.csv"); - public static final String BIOENERGY_2GEN_DEMAND_FILE = getProperty("BIOENERGY_2GEN_DEMAND_FILE", DATA_DIR + File.separator + BIOENERGY_2GEN_DEMAND_FILENAME); - public static final String BIOENERGY_1GEN_FUTURE_DEMAND_FILENAME = getProperty("BIOENERGY_FUTURE_DEMAND_FILENAME", "bioenergy_gen1.csv"); - public static final String BIOENERGY_1GEN_FUTURE_DEMAND_FILE = getProperty("BIOENERGY_FUTURE_DEMAND_FILE", DATA_DIR + File.separator + BIOENERGY_1GEN_FUTURE_DEMAND_FILENAME); - public static final String TRADE_BARRIERS_FILENAME = getProperty("TRADE_BARRIERS_FILENAME", "tradeBarriers.csv"); - public static final String TRADE_BARRIERS_FILE = getProperty("TRADE_BARRIERS_FILE", DATA_DIR + File.separator + TRADE_BARRIERS_FILENAME); - public static final String TRADE_DISTORTIONS_FILE = DATA_DIR + File.separator + "tradeDistortions.csv"; - public static final String STOCKS_FILE = DATA_DIR + File.separator + "global_stocks.csv"; - public static final String FAO_CONSUMPTION_FILE = DATA_DIR + File.separator + "fao_consump.csv"; - public static final String COUNTRY_GROUPING_FILE = DATA_DIR + File.separator + "country_groups.csv"; - public static final String OTHER_WATER_USES_FILE = DATA_DIR + File.separator + "other_water_uses.csv"; - public static final String BASE_DEMAND_FRACT_FILE = DATA_DIR + File.separator + "base_demand_fracts.csv"; - public static final String SHOCKS_PARAMETER_FILE = OUTPUT_DIR + File.separator+ "shocks.csv"; - public static final String SUBSIDY_RATE_FILENAME = getProperty("SUBSIDY_RATE_FILENAME", "subsidyrates.csv"); - public static final String SUBSIDY_RATE_FILE = getProperty("SUBSIDY_RATE_FILE", DATA_DIR + File.separator + SUBSIDY_RATE_FILENAME); - public static final String ANIMAL_RATES_FILE = DATA_DIR + File.separator + "animal_numbers.csv";; - public static final String INITIAL_CONSUMER_PRICE_FILE = DATA_DIR + File.separator + "consumerprices.csv";; - public static final String GDP_FRACTIONS_FILE = DATA_DIR + File.separator + "agriculturalGdpFraction.csv"; - - // yield data - public static final String YIELD_DIR_BASE = getProperty("YIELD_DIR_BASE"); - public static final String YIELD_DIR_TOP = getProperty("YIELD_DIR_TOP"); - public static final String YIELD_DIR = getProperty("YIELD_DIR", YIELD_DIR_BASE + File.separator + YIELD_DIR_TOP); - public static final int LPJG_MONITOR_TIMEOUT_SEC = getIntProperty("LPJG_MONITOR_TIMEOUT", 60*60*2); - public static final String ANPP_FILENAME = getProperty("ANPP_FILENAME", "anpp.out"); - public static final String YIELD_FILENAME = getProperty("YIELD_FILENAME", "yield.out"); - public static final boolean PASTURE_FERT_RESPONSE_FROM_LPJ = getBooleanProperty("PASTURE_FERT_RESPONSE_FROM_LPJ", false);; - - public static final double CALIB_FACTOR_CEREAL_C3 = getDoubleProperty("CALIB_FACTOR_CEREAL_C3", 1.046); - public static final double CALIB_FACTOR_CEREAL_C4 = getDoubleProperty("CALIB_FACTOR_CEREAL_C4", 0.654); - public static final double CALIB_FACTOR_MISCANTHUS = getDoubleProperty("CALIB_FACTOR_MISCANTHUS", 2.148); - public static final double CALIB_FACTOR_RICE = getDoubleProperty("CALIB_FACTOR_RICE", 0.972); - public static final double CALIB_FACTOR_OILCROPS = getDoubleProperty("CALIB_FACTOR_OILCROPS", 0.578); - public static final double CALIB_FACTOR_PULSES = getDoubleProperty("CALIB_FACTOR_PULSES", 0.686); - public static final double CALIB_FACTOR_STARCHY_ROOTS = getDoubleProperty("CALIB_FACTOR_STARCHY_ROOTS",4.560); - public static final double CALIB_FACTOR_FRUITVEG = getDoubleProperty("CALIB_FACTOR_FRUITVEG",3.526); - public static final double CALIB_FACTOR_SUGAR = getDoubleProperty("CALIB_FACTOR_SUGAR", 11.909); - - // These are production prices in PLUM style feed equivalent terms - public static final double INITIAL_PRICE_SHIFT = getDoubleProperty("INITIAL_PRICE_SHIFT", 1.0); - public static final double INITAL_PRICE_WHEAT = getDoubleProperty("INITAL_PRICE_WHEAT", 0.157 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_MAIZE = getDoubleProperty("INITAL_PRICE_MAIZE", 0.152 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_RICE = getDoubleProperty("INITAL_PRICE_RICE", 0.182 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_OILCROPS = getDoubleProperty("INITAL_PRICE_OILCROPS", (0.820 * .4 + 0.314 * .6) * 0.3 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_PULSES = getDoubleProperty("INITAL_PRICE_PULSES", 0.2 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_STARCHYROOTS = getDoubleProperty("INITAL_PRICE_STARCHYROOTS", 0.1 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_MONOGASTRICS = getDoubleProperty("INITAL_PRICE_MONOGASTRICS", 0.4 * 0.5 * ModelConfig.INITIAL_PRICE_SHIFT); // quantities is in feed equivalent term (0.4 is weighted average price per feed, and 0.5 accounts for mark-up for additional processing) - public static final double INITAL_PRICE_RUMINANTS = getDoubleProperty("INITAL_PRICE_RUMINANTS", 0.1 * 0.6 * ModelConfig.INITIAL_PRICE_SHIFT); // quantities is in feed equivalent term - public static final double INITAL_PRICE_ENERGYCROPS = getDoubleProperty("INITAL_PRICE_ENERGYCROPS", 0.04 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_FRUITVEG = getDoubleProperty("INITAL_PRICE_FRUITVEG", 0.1 * ModelConfig.INITIAL_PRICE_SHIFT); - public static final double INITAL_PRICE_SUGAR = getDoubleProperty("INITAL_PRICE_SUGAR", 0.02 * ModelConfig.INITIAL_PRICE_SHIFT); - - // These are initial demand system prices in 2000 kcal terms - public static final double INITAL_DEMAND_PRICE_CEREALS = getDoubleProperty("INITAL_DEMAND_PRICE_CEREALS", 120.2365); - public static final double INITAL_DEMAND_PRICE_OILCROPS_PULSES = getDoubleProperty("INITAL_DEMAND_PRICE_OILCROPS_PULSES", 147.4032); - public static final double INITAL_DEMAND_PRICE_STARCHYROOTS = getDoubleProperty("INITAL_DEMAND_PRICE_STARCHYROOTS", 543.5512); - public static final double INITAL_DEMAND_PRICE_MONOGASTRICS = getDoubleProperty("INITAL_DEMAND_PRICE_MONOGASTRICS", 1243.899); - public static final double INITAL_DEMAND_PRICE_RUMINANTS = getDoubleProperty("INITAL_DEMAND_PRICE_RUMINANTS", 1043.901); - public static final double INITAL_DEMAND_PRICE_FRUITVEG = getDoubleProperty("INITAL_DEMAND_PRICE_FRUITVEG", 3381.178); - public static final double INITAL_DEMAND_PRICE_SUGAR = getDoubleProperty("INITAL_DEMAND_PRICE_SUGAR", 168.9247); - - // Spatial (gridded) data - public static final double CELL_SIZE_X = getDoubleProperty("CELL_SIZE_X", 0.5); - public static final double CELL_SIZE_Y = getDoubleProperty("CELL_SIZE_Y", CELL_SIZE_X); - public static final String SPATIAL_DIR_NAME = getProperty("SPATIAL_DIR_NAME", "halfdeg"); - public static final String SPATIAL_DATA_DIR = getProperty("SPATIAL_DATA_DIR", DATA_DIR + File.separator + SPATIAL_DIR_NAME); - public static final String INITAL_LAND_COVER_FILENAME = getProperty("INITAL_LAND_COVER_FILENAME", "hurtt_2010.txt"); - public static final String INITAL_LAND_COVER_FILE = SPATIAL_DATA_DIR + File.separator + INITAL_LAND_COVER_FILENAME; - public static final String COUNTRY_BOUNDARY_FILE = SPATIAL_DATA_DIR + File.separator + "country_boundaries.asc"; - public static final String IRRIGATION_COST_FILE = SPATIAL_DATA_DIR + File.separator + "irrigation_cost.asc"; - public static final String IRRIGATION_CONSTRAINT_FILE = SPATIAL_DATA_DIR + File.separator + "blue_water_available_pseudoCRU_rcp8p5_2004_2013_grid_allhdyro_mm.txt"; - public static final String FPU_BOUNDARIES_FILE = SPATIAL_DATA_DIR + File.separator + "FPU.asc"; - 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 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"; - public static final String YIELDSHOCKS_PARAMETER_FILE = getProperty("YIELDSHOCKS_PARAMETER_FILE", OUTPUT_DIR + File.separator+ "yieldshocks.csv"); - public static final String PRICESHOCKS_PARAMETER_FILE = getProperty("PRICESHOCKS_PARAMETER_FILE", OUTPUT_DIR + File.separator+ "priceshocks.csv"); - public static final String EXPORT_RESTRICTIONS_FILE = getProperty("EXPORT_RESTRICTIONS_FILE", OUTPUT_DIR + File.separator+ "exportrestictions.csv"); - - // Wood/carbon data - public static final String FOREST_DIR = SPATIAL_DATA_DIR + File.separator + "forestry"; - public static final String WOOD_AND_CARBON_DIR = getProperty("WOOD_AND_CARBON_DIR"); - //public static final String CARBON_FLUX_FILE = FOREST_DIR + File.separator + "carbon_flux_"; - public static final String WOOD_YIELD_FRST_TO_AGRI_FILENAME = "wood_yield_forest_to_agri.dat"; - public static final String WOOD_YIELD_FRST_TO_FRST_FILENAME = "wood_yield_forest_to_forest.dat"; - public static final String WOOD_YIELD_NTRL_TO_AGRI_FILENAME = "wood_yield_ntrl_to_agri.dat"; - public static final String WOOD_YIELD_NTRL_TO_FRST_FILENAME = "wood_yield_ntrl_to_forest.dat"; - public static final String CARBON_LUC_FILENAME = "carbon_luc.out"; - public static final String CARBON_NEE_FILENAME = "carbon_nee.out"; - public static final String NATURAL_FOREST_GROWTH_FILENAME = "growth_natural.out"; - public static final String LAND_COVER_AGE_DIST_FILENAME = SPATIAL_DATA_DIR + File.separator + "land_cover_age_dist.txt"; - 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", 8.0); - public static final int CARBON_WOOD_MAX_TIME = getIntProperty("CARBON_WOOD_AGE_CLASSES", 165); - - // Output - public static final String LAND_COVER_OUTPUT_FILE = OUTPUT_DIR + File.separator + "lc.txt"; - public static final String PRICES_OUTPUT_FILE = OUTPUT_DIR + File.separator + "prices.txt"; - public static final String DEMAND_OUTPUT_FILE = OUTPUT_DIR + File.separator + "demand.txt"; - public static final String DOMESTIC_OUTPUT_FILE = OUTPUT_DIR + File.separator + "domestic.txt"; - public static final String COUNTRY_DEMAND_FILE = OUTPUT_DIR + File.separator + "countryDemand.txt"; - public static final String DEMAND_OPTIMISATION_OUTPUT_FILE = OUTPUT_DIR + File.separator + "countryDemandOpt.txt"; - public static final String FOOD_BALANCE_SHEET_FILE = OUTPUT_DIR + File.separator + "fbs.txt"; - public static final String ANIMAL_NUMBERS_OUTPUT_FILE = OUTPUT_DIR + File.separator + "animals.txt";; - - public static final boolean OUTPUT_FOR_LPJG = getBooleanProperty("OUTPUT_FOR_LPJG", true); - public static final boolean INTERPOLATE_OUTPUT_YEARS = getBooleanProperty("INTERPOLATE_OUTPUT_YEARS", true); - - // Calibration related stuff - public static final boolean IS_CALIBRATION_RUN = getBooleanProperty("IS_CALIBRATION_RUN", false); - public static final String CALIB_DIR = IS_CALIBRATION_RUN ? OUTPUT_DIR : getProperty("CALIB_DIR", OUTPUT_DIR); - public static final int END_FIRST_STAGE_CALIBRATION = getIntProperty("END_FIRST_STAGE_CALIBRATION", 10); - public static final String SERIALIZED_LAND_USE_FILENAME = "landUseRaster.ser"; - public static final String SERIALIZED_CROP_USAGE_FILENAME = "countryCropUsages.ser"; - public static final String SERIALIZED_INTERNATIONAL_MARKET_FILENAME = "internationalMarket.ser"; - public static final String SERIALIZED_WOOD_USAGE_FILENAME = "countryWoodUsage.ser"; - public static final String SERIALIZED_LAND_COVER_FILENAME = "landCoverData.dat"; - public static final String SERIALIZED_LAND_USE_FILE = CALIB_DIR + File.separator + SERIALIZED_LAND_USE_FILENAME; - public static final String SERIALIZED_CROP_USAGE_FILE = CALIB_DIR + File.separator + SERIALIZED_CROP_USAGE_FILENAME; - public static final String SERIALIZED_INTERNATIONAL_MARKET_FILE = CALIB_DIR + File.separator + SERIALIZED_INTERNATIONAL_MARKET_FILENAME; - public static final String SERIALIZED_LAND_COVER_FILE = CALIB_DIR + File.separator + SERIALIZED_LAND_COVER_FILENAME; - public static final String SERIALIZED_WOOD_USAGE_FILE = CALIB_DIR + File.separator + SERIALIZED_WOOD_USAGE_FILENAME; - public static final String CHECKPOINT_LAND_USE_FILE = getProperty("CHECKPOINT_LAND_USE_FILE", OUTPUT_DIR + File.separator + SERIALIZED_LAND_USE_FILENAME); - public static final String CHECKPOINT_CROP_USAGE_FILE = getProperty("CHECKPOINT_CROP_USAGE_FILE", OUTPUT_DIR + File.separator + SERIALIZED_CROP_USAGE_FILENAME); - 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 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); - public static final String CLUSTERED_YIELD_FILE = getProperty("CLUSTERED_YIELD_FILE", CALIB_DIR + File.separator + "cluster.asc"); - public static final boolean GENERATE_NEW_YIELD_CLUSTERS = getBooleanProperty("GENERATE_NEW_YIELD_CLUSTERS", IS_CALIBRATION_RUN); - - - // Temporal configuration - public static final int START_TIMESTEP = getIntProperty("START_TIMESTEP", 0); - public static final int END_TIMESTEP = getIntProperty("END_TIMESTEP", 90); - public static final int TIMESTEP_SIZE = getIntProperty("TIMESTEP_SIZE", 1); - public static final int BASE_YEAR = getIntProperty("BASE_YEAR", 2010); - - // Import export limits - public static final double ANNUAL_MAX_IMPORT_CHANGE = getDoubleProperty("ANNUAL_MAX_IMPORT_CHANGE", 0.05); - public static final double MAX_IMPORT_CHANGE = getDoubleProperty("MAX_IMPORT_CHANGE", ANNUAL_MAX_IMPORT_CHANGE*TIMESTEP_SIZE); - - // Fertiliser application rates in kg/ha - public static final double MIN_FERT_AMOUNT = getDoubleProperty("MIN_FERT_AMOUNT", 0.0); - public static final double MID_FERT_AMOUNT = getDoubleProperty("MID_FERT_AMOUNT", 200.0); - public static final double MAX_FERT_AMOUNT = getDoubleProperty("MAX_FERT_AMOUNT", 1000.0); - public static final int FERT_AMOUNT_PADDING = getIntProperty("FERT_AMOUNT_PADDING", 4);; - - // SSP shift parameters - public static final double SSP_POPULATION_FACTOR = getDoubleProperty("SSP_POPULATION_FACTOR", 1.0); - public static final double SSP_GDP_PC_FACTOR = getDoubleProperty("SSP_GDP_PC_FACTOR", 1.0); - - // Other model parameters - public static final boolean CHANGE_DEMAND_YEAR = IS_CALIBRATION_RUN ? false : getBooleanProperty("CHANGE_DEMAND_YEAR", true); - public static final double DIETARY_CLOSURE = getDoubleProperty("DIETARY_CLOSURE", 0.25); // Amount diet converges in DIETARY_CLOSURE_GDP_CHANGE rate of GDP shift - public static final double DIETARY_CLOSURE_GDP_CHANGE = getDoubleProperty("DIETARY_CLOSURE_GDP_CHANGE", 2.0); // 2 is double of GDP - public static final double DIETARY_CLOSURE_PARAM = getDoubleProperty("DIETARY_CLOSURE_PARAM", Math.log((1-DIETARY_CLOSURE))/DIETARY_CLOSURE_GDP_CHANGE); // Zero is no dietary closure, number specifies closure rate for changes in GDP per capita, e.g. Math.log(0.5)/2 - public static final String SSP_SCENARIO = getProperty("SSP_SCENARIO", "SSP1_v9_130325"); - public static final ModelFitType DEMAND_ANIMAL_PROD_FIT = ModelFitType.findByName(getProperty("DEMAND_ANIMAL_PROD_FIT", "loglinear")); - public static final ModelFitType DEMAND_NON_ANIMAL_PROD_FIT = ModelFitType.findByName(getProperty("DEMAND_NON_ANIMAL_PROD_FIT", "loglinear")); - public static final boolean LIMIT_DEMAND_FRACTION = getBooleanProperty("LIMIT_DEMAND_FRACTION", true); - public static final boolean DEMAND_FRACT_BY_COST = getBooleanProperty("DEMAND_FRACT_BY_COST", false);; - public static final double MAX_INCOME_PROPORTION_FOOD_SPEND = getDoubleProperty("MAX_INCOME_PROPORTION_FOOD_SPEND", 0.7); - - public static final double PASTURE_HARVEST_FRACTION = getDoubleProperty("PASTURE_HARVEST_FRACTION", 0.5); - public static final double MEAT_EFFICIENCY = getDoubleProperty("MEAT_EFFICIENCY", 1.0); // 'meat' is includes feed conversion ratio already, this is tech. change or similar - public static final double IRRIGIATION_EFFICIENCY = getDoubleProperty("IRRIGIATION_EFFICIENCY", 0.5); - public static final int ELLIOTT_BASEYEAR = 2010; - public static final double ENVIRONMENTAL_WATER_CONSTRAINT = getDoubleProperty("ENVIRONMENTAL_WATER_CONSTRAINT", 0.5); // change with care, as due to normalisation it might not have the impact you first imagine - public static final double OTHER_WATER_USE_FACTOR = getDoubleProperty("OTHER_WATER_USE_FACTOR", 1.0); - public static final boolean USE_BLUE_WATER_FILE_IRRIG_CONSTRAINT = getBooleanProperty("USE_BLUE_WATER_FILE_IRRIG_CONSTRAINT", false);; - - public static final double LAND_CHANGE_COST = getDoubleProperty("LAND_CHANGE_COST", 0.2); - public static final double CROP_TO_PASTURE_COST_FACTOR = getDoubleProperty("CROP_TO_PASTURE_COST_FACTOR", 1.0); - public static final double AGRI_LAND_EXPANSION_COST_FACTOR = getDoubleProperty("AGRI_LAND_EXPANSION_COST_FACTOR", 1.0); - - public static final double CROP_INCREASE_COST = getDoubleProperty("CROP_INCREASE_COST", 0.05 * LAND_CHANGE_COST * AGRI_LAND_EXPANSION_COST_FACTOR); - 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 AGRI_EXPANSION_COST_BASE_MANAGED_FOREST = getDoubleProperty("AGRI_EXPANSION_COST_BASE_MANAGED_FOREST", 0.5 * LAND_CHANGE_COST * AGRI_LAND_EXPANSION_COST_FACTOR); - - 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); - - public static final boolean USE_BIOENERGY_TRAJECTORY = getBooleanProperty("USE_BIOENERGY_TRAJECTORY", true); // false is the old style, i.e. BIOENERGY_CHANGE_ANNUAL_RATE, BIOENERGY_CHANGE_START_YEAR and BIOENERGY_CHANGE_END_YEAR - // original way to specify changes in bioenergy - public static final double BIOENERGY_CHANGE_ANNUAL_RATE = IS_CALIBRATION_RUN ? 0.0 : getDoubleProperty("BIOENERGY_CHANGE_ANNUAL_RATE", 0.10); // 6.2/2.31/20 - public static final int BIOENERGY_CHANGE_START_YEAR = getIntProperty("BIOENERGY_CHANGE_START_YEAR", 2010); - public static final int BIOENERGY_CHANGE_END_YEAR = getIntProperty("BIOENERGY_CHANGE_END_YEAR", 2060); - // newer way - public static final boolean ENABLE_GEN2_BIOENERGY = getBooleanProperty("ENABLE_GEN2_BIOENERGY", true); - public static final String BIOENERGY_DEMAND_SCENARIO = getProperty("BIOENERGY_DEMAND_SCENARIO", "SSP2_RCP45"); - public static final String GEN2_BIOENERGY_MODEL = getProperty("GEN2_BIOENERGY_MODEL", "ENSEMBLE-MEAN"); - public static final double BIOENERGY_DEMAND_SHIFT = IS_CALIBRATION_RUN ? 1.0 : getDoubleProperty("BIOENERGY_DEMAND_SHIFT", 1.0); -// public static final double BIOENERGY_HEATING_VALUE_GJ_PER_T = getDoubleProperty("BIOENERGY_HEATING_VALUE_GJ_PER_T", 17.5); // GJ per t DM - - //Dietary stuff - public static final double RUMINANT_CHANGE_ANNUAL_RATE = getDoubleProperty("RUMINANT_CHANGE_ANNUAL_RATE", 0.0); - public static final double MONOGASTRIC_CHANGE_ANNUAL_RATE = getDoubleProperty("MONOGASTRIC_CHANGE_ANNUAL_RATE", 0.00); - public static final boolean CONSTANT_DIET_LOW_INCOME = getBooleanProperty("CONSTANT_DIET_LOW_INCOME", false); - public static final boolean CONSTANT_DIET_HIGH_INCOME = getBooleanProperty("CONSTANT_DIET_HIGH_INCOME", false); - //below should all sum to zero - public static final double CEREALS_SUB_PROPORTION = getDoubleProperty("CEREALS_SUB_PROPORTION", 0.3); - public static final double OILCROPSPULSES_SUB_PROPORTION = getDoubleProperty("OILCROPSPULSES_SUB_PROPORTION", 0.25); - public static final double STARCHY_ROOTS_SUB_PROPORTION = getDoubleProperty("STARCHY_ROOTS_SUB_PROPORTION", 0.25); - public static final double FRUITVEG_SUB_PROPORTION = getDoubleProperty("FRUITVEG_SUB_PROPORTION", 0.1); - public static final double SUGAR_SUB_PROPORTION = getDoubleProperty("SUGAR_SUB_PROPORTION", 0.1); - - public static final double MARKET_LAMBA = getDoubleProperty("MARKET_LAMBA", 0.4); // controls international market price adjustment rate - public static final boolean PRICE_UPDATE_BY_MARKET_IMBALANCE = getBooleanProperty("PRICE_UPDATE_BY_MARKET_IMBALANCE", false);; - public static final double MAX_PRICE_INCREASE = getDoubleProperty("MAX_PRICE_INCREASE", 1.5); - public static final double MAX_PRICE_DECREASE = getDoubleProperty("MAX_PRICE_DECREASE", .75); - public static final int DEMAND_RECALC_MAX_ITERATIONS = IS_CALIBRATION_RUN ? 0 : getIntProperty("DEMAND_RECALC_MAX_ITERATIONS", 1); // 0 is original behaviour - public static final boolean DEMAND_RECALC_ON_NEGATIVE_STOCK = IS_CALIBRATION_RUN ? false : getBooleanProperty("DEMAND_RECALC_ON_NEGATIVE_STOCK", false); - - public static final double POPULATION_AGGREG_LIMIT = getDoubleProperty("POPULATION_AGGREG_LIMIT", 30.0); // in millions, smaller countries are aggregated on a regional basis - public static final boolean PREDEFINED_COUNTRY_GROUPING = getBooleanProperty("PREDEFINED_COUNTRY_GROUPING", true); - - // 1536.7 Mha is all cropland in LUH2, or 1555.6 Mha from FAO. country_data[Country =="World" & Year == 2011, list(arable+ perm_crops)] - // 1377.6 Mha is area of crops in FAO crop production. con_prod_unadj[Country =="World" & Item %in% itemDetails[produced==TRUE]$Item & Year == 2011, sum(prod_area, na.rm=TRUE)] - // 1105.6 Mha is harvested in crops we represent. crop_prod[Country =="World" & Item %in% itemGroupMapping$cropProdItem & Year == 2011, sum(area)] - // extract values by running AltLURead.R and common.R - // So we do not represent crops covering (1341.911-1239.03)/1547.464=6.65% of cropland. Additionally 10.3% of cropland is set aside, fallow or failed crops, - public static final double UNHANDLED_CROP_RATE = getDoubleProperty("UNHANDLED_CROP_RATE", 0.0665); // mostly forage crops - public static final double SETASIDE_RATE = getDoubleProperty("SETASIDE_RATE", 0.103); // includes aside, fallow and failed cropland areas - - 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.0003); - 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", 1.4); // $500/t, 18% N/t - public static final double FERTILISER_MAX_COST = FERTILISER_COST_PER_T * MAX_FERT_AMOUNT/1000; - - public static final double DOMESTIC_PRICE_MARKUP = getDoubleProperty("DOMESTIC_PRICE_MARKUP", 1.0); - public static final double TRANSPORT_LOSSES = getDoubleProperty("TRANSPORT_LOSSES", 0.05); // in international trade - public static final double TRANSPORT_COST = getDoubleProperty("TRANSPORT_COST", 0.1); // 10% transport cost - public static final double TRADE_BARRIER_FACTOR_DEFAULT = getDoubleProperty("TRADE_BARRIER_FACTOR_DEFAULT", 0.2); // price factor in international trade, transport cost and real trade barriers - public static final double TRADE_BARRIER_MULTIPLIER = getDoubleProperty("TRADE_BARRIER_MULTIPLIER", 1.0); - public static final boolean ACTIVE_TRADE_BARRIERS = getBooleanProperty("ACTIVE_TRADE_BARRIERS", true); // if set to true read in barrier information from file, otherwise use default as above - public static final double TRADE_BARRIER_ENERGY_CROPS = getDoubleProperty("TRADE_BARRIER_ENERGY_CROPS", 0.01); // price factor in international trade, transport cost and real trade barriers - public static final boolean SHOCKS_POSSIBLE = getBooleanProperty("SHOCKS_POSSIBLE", false); - public static final double YIELD_SHOCK_MAGNIFIER = getDoubleProperty("YIELD_SHOCK_MAGNIFIER", 1.0); - - public static final boolean PROTECTED_AREAS_ENABLED = getBooleanProperty("PROTECTED_AREAS_ENABLED", true); - public static final double MIN_NATURAL_RATE = getDoubleProperty("MIN_NATURAL_RATE", 0.05); - public static final double MAX_CHINA_LAND_EXPANSION_RATE = getDoubleProperty("MAX_CHINA_LAND_EXPANSION_RATE", 0.011*0.4); // 1.1% max forest change 40% of natural land is forest - - public static final boolean DEBUG_JUST_DEMAND_OUTPUT = getBooleanProperty("DEBUG_JUST_DEMAND_OUTPUT", false); - public static final boolean DEBUG_LIMIT_COUNTRIES = getBooleanProperty("DEBUG_LIMIT_COUNTRIES", false); - public static final String DEBUG_COUNTRY_NAME = getProperty("DEBUG_COUNTRY_NAME", "United States of America"); - public static final String GAMS_COUNTRY_TO_SAVE = getProperty("GAMS_COUNTRY_TO_SAVE", "China");; - public static final boolean EXCLUDE_COUNTRIES_IN_LIST = getBooleanProperty("EXCLUDE_COUNTRIES_IN_LIST", false); - public static final String EXCLUDED_COUNTRIES_FILE = DATA_DIR + File.separator + "countries_excluded.csv"; - - public static final double PASTURE_MAX_IRRIGATION_RATE = getDoubleProperty("DEFAULT_MAX_IRRIGATION_RATE", 50.0); // shouldn't need this but some areas crops don't have a value, but was causing them to be selected - public static final int LPJG_TIMESTEP_SIZE = getIntProperty("LPJG_TIMESTEP_SIZE", 5); - public static final int LPJ_YEAR_OFFSET = getIntProperty("LPJ_YEAR_OFFSET", 0);; - - public static final int NUM_YIELD_CLUSTERS = getIntProperty("NUM_YIELD_CLUSTERS", 8000); - 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 - - // Protected areas forcing parameters - public static final boolean FORCE_PROTECTED_AREAS = IS_CALIBRATION_RUN ? false : getBooleanProperty("FORCE_PROTECTED_AREAS", false); - public static final int FORCE_PROTECTED_AREAS_START_YEAR = getIntProperty("FORCE_PROTECTED_AREAS_START_YEAR", 2020); - public static final int FORCE_PROTECTED_AREAS_END_YEAR = getIntProperty("FORCE_PROTECTED_AREAS_END_YEAR", 2040); - public static final double CONSTANT_PROTECTED_AREA_RATE = getDoubleProperty("CONSTANT_PROTECTED_AREA_RATE", Double.NaN); - public static final boolean HALFEARTH = getBooleanProperty("HALFEARTH", false); - - public static final boolean USE_CRAFTY_COUNTRIES = getBooleanProperty("USE_CRAFTY_COUNTRIES", false); - public static final String CRAFTY_PRODUCTION_DIR = getProperty("CRAFTY_PRODUCTION_DIR", OUTPUT_DIR + File.separator + "crafty"); - - public static final boolean EXTRAPOLATE_YIELD_FERT_RESPONSE = getBooleanProperty("EXTRAPOLATE_YIELD_FERT_RESPONSE", false); - - public static final boolean ADJUST_DIET_PREFS = getBooleanProperty("ADJUST_DIET_PREFS", false); - public static final int DIET_CHANGE_START_YEAR = getIntProperty("DIET_CHANGE_START_YEAR", 2020); - public static final int DIET_CHANGE_END_YEAR = getIntProperty("DIET_CHANGE_END_YEAR", 2040); - public static final String TARGET_DIET_FILE = getProperty("TARGET_DIET_FILE", DATA_DIR + File.separator + "TargetDiet.txt"); - public static final boolean APPLY_EXPORT_TAXES = getBooleanProperty("APPLY_EXPORT_TAXES", false); - public static final double EXPORT_TAX_RATE = getDoubleProperty("EXPORT_TAX_RATE", 1.0); - public static final double EXPORT_TAX_THRESHOLD = getDoubleProperty("EXPORT_TAX_THRESHOLD", 0.1); - - // 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 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 double INIT_CARBON_PRICE = getDoubleProperty("INIT_CARBON_PRICE", 0.02); // $1000/tC-eq - 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 double INIT_WOOD_PRICE = getDoubleProperty("INIT_WOOD_PRICE", 0.4); // $1000/tC-eq - public static final double INIT_WOOD_STOCK = getDoubleProperty("INIT_WOOD_STOCK", 1000.0); // tC-eq - public static final double FOREST_MANAGEMENT_COST = getDoubleProperty("FOREST_MANAGEMENT_COST", 0.05); // $1000/ha/year - 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 ROUNDWOOD_DEMAND_ELASTICITY = getDoubleProperty("ROUNDWOOD_DEMAND_ELASTICITY", -0.21956); - public static final double FUELWOOD_DEMAND_ELASTICITY = getDoubleProperty("FUELDWOOD_DEMAND_ELASTICITY", -0.22787); - public static final double FOREST_ESTABLISHMENT_COST = getDoubleProperty("FOREST_ESTABLISHMENT_COST", 2.0); // $1000/ha - 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", 2.688e-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.08); - 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 int CARBON_HORIZON = getIntProperty("CARBON_HORIZON", 30); - public static final double INFRASTRUCTURE_EXPANSION_COST = getDoubleProperty("INFRASTRUCTURE_EXPANSION_COST", 0.1); //$1000/tC - public static final double WOOD_TRADE_BARRIER = getDoubleProperty("WOOD_TRADE_BARRIER", 0.05); //$1000/tC -} +package ac.ed.lurg; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Properties; +import java.lang.Long; + +import ac.ed.lurg.shock.parameterShocksReader; +import ac.ed.lurg.types.ModelFitType; +import ac.ed.lurg.types.PriceType; +import ac.ed.lurg.utils.LogWriter; + +public class ModelConfig { + + private Properties configFile; + private static ModelConfig modelConfig; + public static final String CONFIG_FILE = System.getProperty("CONFIG_FILE"); + + private static parameterShocksReader shocksReader; + + private ModelConfig() { + configFile = new Properties(); + try { + System.out.println("Config. file is " + CONFIG_FILE); + if (CONFIG_FILE != null) + configFile.load(new FileInputStream(CONFIG_FILE)); + } + catch (IOException e) { + System.err.println("Problems reading config file"); + System.err.println(e.getMessage()); + } + } + + public static String getSetupDetails() { + String buildVerion = System.getProperty("BUILDVER"); + StringBuffer sb = new StringBuffer("Build version: " + buildVerion + "\n"); + + Properties props = getModelConfig().configFile; + Enumeration<?> em = props.keys(); + while(em.hasMoreElements()) { + String str = (String) em.nextElement(); + sb.append(str + ": " + props.get(str) + "\n"); + } + + return sb.toString(); + } + + private static ModelConfig getModelConfig() { + if (modelConfig == null) + modelConfig = new ModelConfig(); + + return modelConfig; + } + + private static String getProperty(String prop) { + return getModelConfig().getProp(prop); + } + private static String getProperty(String prop, String defaultString) { + String propValue = getProperty(prop); + return propValue == null ? defaultString : propValue; + } + private String getProp(String prop) { + return configFile.getProperty(prop); + } + + private static Integer getIntProperty(String prop, Integer defaultInt) { + Integer propValue = getModelConfig().getIntProp(prop); + return propValue == null ? defaultInt : propValue; + } + private Integer getIntProp(String prop) { + String v = configFile.getProperty(prop); + return v==null ? null : Integer.valueOf(v); + } + + @SuppressWarnings("unused") + private static Long getLongProperty(String prop, Long defaultLong) { + Long propValue = getModelConfig().getLongProp(prop); + return propValue == null ? defaultLong : propValue; + } + private Long getLongProp(String prop) { + String v = configFile.getProperty(prop); + return v==null ? null : Long.valueOf(v); + } + + private static Double getDoubleProperty(String prop, Double defaultDouble) { + Double propValue = getModelConfig().getDoubleProp(prop); + return propValue == null ? defaultDouble : propValue; + } + private Double getDoubleProp(String prop) { + String v = configFile.getProperty(prop); + return v==null ? null : Double.valueOf(v); + } + + private static Boolean getBooleanProperty(String prop, Boolean defaultBoolean) { + return getModelConfig().getBooleanProp(prop, defaultBoolean); + } + private boolean getBooleanProp(String prop, Boolean defaultBoolean) { + String v = configFile.getProperty(prop); + return v==null ? defaultBoolean : Boolean.valueOf(v); + } + + public static double updateParameterForShocks(int year, String parameter) { + + Double value = null; + try { + value = getModelConfig().getClass().getField(parameter).getDouble(parameter); + + if(SHOCKS_POSSIBLE) { + + if (shocksReader == null) { + shocksReader = new parameterShocksReader(","); + shocksReader.read(SHOCKS_PARAMETER_FILE); + } + Double updatedValue = shocksReader.queryForParameter(year, parameter); + + if(updatedValue !=null) + value = updatedValue; + } + + } + catch (IllegalAccessException | NoSuchFieldException e) { + LogWriter.printlnError("cannot find parameter in model config to shock: " + parameter); + LogWriter.print(e); + } + return value; + } + + + public static final boolean SUPPRESS_STD_OUTPUT = getBooleanProperty("SUPPRESS_STD_OUTPUT", Boolean.FALSE); + + // Directory information + public static final String BASE_DIR = getProperty("BASE_DIR"); // this must to be set in config file + public static final String OUTPUT_DIR = getProperty("OUTPUT_DIR", "."); + 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 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"); + + // Country (non-gridded) data + public static final boolean DEMAND_FROM_FILE = getBooleanProperty("DEMAND_FROM_FILE", false); // used in hindcasting + public static final boolean PRICE_ELASTIC_DEMAND = getBooleanProperty("PRICE_ELASTIC_DEMAND", true); + public static final boolean DONT_REBASE_DEMAND = getBooleanProperty("DONT_REBASE_DEMAND", false);; + public static final String DEMAND_CURVES_FILE = getProperty("DEMAND_CURVES_FILE", DATA_DIR + File.separator + "com_curves.csv"); // either DEMAND_CURVES_FILE or DEMAND_CONSUMPTION_FILE is used, but not both + public static final String DEMAND_CONSUMPTION_FILE = getProperty("DEMAND_CONSUMPTION_FILE", DATA_DIR + File.separator + "hist_comsump.csv"); + public static final PriceType PRICE_CALCULATION = PriceType.findByName(getProperty("PRICE_CALCULATION", "weightedImportsExports")); + public static final String SSP_FILENAME = getProperty("SSP_FILENAME", "ssp.csv"); + public static final String SSP_FILE = getProperty("SSP_FILE", DATA_DIR + File.separator + SSP_FILENAME); + public static final String BASELINE_CONSUMP_FILE = DATA_DIR + File.separator + "base_consump.csv"; + public static final String CALORIE_PER_T_FILE = DATA_DIR + File.separator + "calories_per_t.csv"; + public static final String COUNTRY_CODES_FILE = DATA_DIR + File.separator + "country_codes4.csv"; + public static final String COUNTRY_DATA_FILE = DATA_DIR + File.separator + "country_data.csv"; + public static final String NET_IMPORTS_FILE = DATA_DIR + File.separator + "net_imports.csv"; + public static final String BIOENERGY_1GEN_BASE_DEMAND_FILE = DATA_DIR + File.separator + "bio_demand.csv"; + public static final String BIOENERGY_2GEN_DEMAND_FILENAME = getProperty("BIOENERGY_2GEN_DEMAND_FILENAME", "bioenergy_gen2_iiasa.csv"); + public static final String BIOENERGY_2GEN_DEMAND_FILE = getProperty("BIOENERGY_2GEN_DEMAND_FILE", DATA_DIR + File.separator + BIOENERGY_2GEN_DEMAND_FILENAME); + public static final String BIOENERGY_1GEN_FUTURE_DEMAND_FILENAME = getProperty("BIOENERGY_FUTURE_DEMAND_FILENAME", "bioenergy_gen1.csv"); + public static final String BIOENERGY_1GEN_FUTURE_DEMAND_FILE = getProperty("BIOENERGY_FUTURE_DEMAND_FILE", DATA_DIR + File.separator + BIOENERGY_1GEN_FUTURE_DEMAND_FILENAME); + public static final String TRADE_BARRIERS_FILENAME = getProperty("TRADE_BARRIERS_FILENAME", "tradeBarriers.csv"); + public static final String TRADE_BARRIERS_FILE = getProperty("TRADE_BARRIERS_FILE", DATA_DIR + File.separator + TRADE_BARRIERS_FILENAME); + public static final String TRADE_DISTORTIONS_FILE = DATA_DIR + File.separator + "tradeDistortions.csv"; + public static final String STOCKS_FILE = DATA_DIR + File.separator + "global_stocks.csv"; + public static final String FAO_CONSUMPTION_FILE = DATA_DIR + File.separator + "fao_consump.csv"; + public static final String COUNTRY_GROUPING_FILE = DATA_DIR + File.separator + "country_groups.csv"; + public static final String OTHER_WATER_USES_FILE = DATA_DIR + File.separator + "other_water_uses.csv"; + public static final String BASE_DEMAND_FRACT_FILE = DATA_DIR + File.separator + "base_demand_fracts.csv"; + public static final String SHOCKS_PARAMETER_FILE = OUTPUT_DIR + File.separator+ "shocks.csv"; + public static final String SUBSIDY_RATE_FILENAME = getProperty("SUBSIDY_RATE_FILENAME", "subsidyrates.csv"); + public static final String SUBSIDY_RATE_FILE = getProperty("SUBSIDY_RATE_FILE", DATA_DIR + File.separator + SUBSIDY_RATE_FILENAME); + public static final String ANIMAL_RATES_FILE = DATA_DIR + File.separator + "animal_numbers.csv";; + public static final String INITIAL_CONSUMER_PRICE_FILE = DATA_DIR + File.separator + "consumerprices.csv";; + public static final String GDP_FRACTIONS_FILE = DATA_DIR + File.separator + "agriculturalGdpFraction.csv"; + + // yield data + public static final String YIELD_DIR_BASE = getProperty("YIELD_DIR_BASE"); + public static final String YIELD_DIR_TOP = getProperty("YIELD_DIR_TOP"); + public static final String YIELD_DIR = getProperty("YIELD_DIR", YIELD_DIR_BASE + File.separator + YIELD_DIR_TOP); + public static final int LPJG_MONITOR_TIMEOUT_SEC = getIntProperty("LPJG_MONITOR_TIMEOUT", 60*60*2); + public static final String ANPP_FILENAME = getProperty("ANPP_FILENAME", "anpp.out"); + public static final String YIELD_FILENAME = getProperty("YIELD_FILENAME", "yield.out"); + public static final boolean PASTURE_FERT_RESPONSE_FROM_LPJ = getBooleanProperty("PASTURE_FERT_RESPONSE_FROM_LPJ", false);; + + public static final double CALIB_FACTOR_CEREAL_C3 = getDoubleProperty("CALIB_FACTOR_CEREAL_C3", 1.046); + public static final double CALIB_FACTOR_CEREAL_C4 = getDoubleProperty("CALIB_FACTOR_CEREAL_C4", 0.654); + public static final double CALIB_FACTOR_MISCANTHUS = getDoubleProperty("CALIB_FACTOR_MISCANTHUS", 2.148); + public static final double CALIB_FACTOR_RICE = getDoubleProperty("CALIB_FACTOR_RICE", 0.972); + public static final double CALIB_FACTOR_OILCROPS = getDoubleProperty("CALIB_FACTOR_OILCROPS", 0.578); + public static final double CALIB_FACTOR_PULSES = getDoubleProperty("CALIB_FACTOR_PULSES", 0.686); + public static final double CALIB_FACTOR_STARCHY_ROOTS = getDoubleProperty("CALIB_FACTOR_STARCHY_ROOTS",4.560); + public static final double CALIB_FACTOR_FRUITVEG = getDoubleProperty("CALIB_FACTOR_FRUITVEG",3.526); + public static final double CALIB_FACTOR_SUGAR = getDoubleProperty("CALIB_FACTOR_SUGAR", 11.909); + + // These are production prices in PLUM style feed equivalent terms + public static final double INITIAL_PRICE_SHIFT = getDoubleProperty("INITIAL_PRICE_SHIFT", 1.0); + public static final double INITAL_PRICE_WHEAT = getDoubleProperty("INITAL_PRICE_WHEAT", 0.157 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_MAIZE = getDoubleProperty("INITAL_PRICE_MAIZE", 0.152 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_RICE = getDoubleProperty("INITAL_PRICE_RICE", 0.182 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_OILCROPS = getDoubleProperty("INITAL_PRICE_OILCROPS", (0.820 * .4 + 0.314 * .6) * 0.3 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_PULSES = getDoubleProperty("INITAL_PRICE_PULSES", 0.2 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_STARCHYROOTS = getDoubleProperty("INITAL_PRICE_STARCHYROOTS", 0.1 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_MONOGASTRICS = getDoubleProperty("INITAL_PRICE_MONOGASTRICS", 0.4 * 0.5 * ModelConfig.INITIAL_PRICE_SHIFT); // quantities is in feed equivalent term (0.4 is weighted average price per feed, and 0.5 accounts for mark-up for additional processing) + public static final double INITAL_PRICE_RUMINANTS = getDoubleProperty("INITAL_PRICE_RUMINANTS", 0.1 * 0.6 * ModelConfig.INITIAL_PRICE_SHIFT); // quantities is in feed equivalent term + public static final double INITAL_PRICE_ENERGYCROPS = getDoubleProperty("INITAL_PRICE_ENERGYCROPS", 0.04 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_FRUITVEG = getDoubleProperty("INITAL_PRICE_FRUITVEG", 0.1 * ModelConfig.INITIAL_PRICE_SHIFT); + public static final double INITAL_PRICE_SUGAR = getDoubleProperty("INITAL_PRICE_SUGAR", 0.02 * ModelConfig.INITIAL_PRICE_SHIFT); + + // These are initial demand system prices in 2000 kcal terms + public static final double INITAL_DEMAND_PRICE_CEREALS = getDoubleProperty("INITAL_DEMAND_PRICE_CEREALS", 120.2365); + public static final double INITAL_DEMAND_PRICE_OILCROPS_PULSES = getDoubleProperty("INITAL_DEMAND_PRICE_OILCROPS_PULSES", 147.4032); + public static final double INITAL_DEMAND_PRICE_STARCHYROOTS = getDoubleProperty("INITAL_DEMAND_PRICE_STARCHYROOTS", 543.5512); + public static final double INITAL_DEMAND_PRICE_MONOGASTRICS = getDoubleProperty("INITAL_DEMAND_PRICE_MONOGASTRICS", 1243.899); + public static final double INITAL_DEMAND_PRICE_RUMINANTS = getDoubleProperty("INITAL_DEMAND_PRICE_RUMINANTS", 1043.901); + public static final double INITAL_DEMAND_PRICE_FRUITVEG = getDoubleProperty("INITAL_DEMAND_PRICE_FRUITVEG", 3381.178); + public static final double INITAL_DEMAND_PRICE_SUGAR = getDoubleProperty("INITAL_DEMAND_PRICE_SUGAR", 168.9247); + + // Spatial (gridded) data + public static final double CELL_SIZE_X = getDoubleProperty("CELL_SIZE_X", 0.5); + public static final double CELL_SIZE_Y = getDoubleProperty("CELL_SIZE_Y", CELL_SIZE_X); + public static final String SPATIAL_DIR_NAME = getProperty("SPATIAL_DIR_NAME", "halfdeg"); + public static final String SPATIAL_DATA_DIR = getProperty("SPATIAL_DATA_DIR", DATA_DIR + File.separator + SPATIAL_DIR_NAME); + public static final String INITAL_LAND_COVER_FILENAME = getProperty("INITAL_LAND_COVER_FILENAME", "hurtt_2010.txt"); + public static final String INITAL_LAND_COVER_FILE = SPATIAL_DATA_DIR + File.separator + INITAL_LAND_COVER_FILENAME; + public static final String COUNTRY_BOUNDARY_FILE = SPATIAL_DATA_DIR + File.separator + "country_boundaries.asc"; + public static final String IRRIGATION_COST_FILE = SPATIAL_DATA_DIR + File.separator + "irrigation_cost.asc"; + public static final String IRRIGATION_CONSTRAINT_FILE = SPATIAL_DATA_DIR + File.separator + "blue_water_available_pseudoCRU_rcp8p5_2004_2013_grid_allhdyro_mm.txt"; + public static final String FPU_BOUNDARIES_FILE = SPATIAL_DATA_DIR + File.separator + "FPU.asc"; + 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 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"; + public static final String YIELDSHOCKS_PARAMETER_FILE = getProperty("YIELDSHOCKS_PARAMETER_FILE", OUTPUT_DIR + File.separator+ "yieldshocks.csv"); + public static final String PRICESHOCKS_PARAMETER_FILE = getProperty("PRICESHOCKS_PARAMETER_FILE", OUTPUT_DIR + File.separator+ "priceshocks.csv"); + public static final String EXPORT_RESTRICTIONS_FILE = getProperty("EXPORT_RESTRICTIONS_FILE", OUTPUT_DIR + File.separator+ "exportrestictions.csv"); + + // Wood/carbon data + public static final String FOREST_DIR = SPATIAL_DATA_DIR + File.separator + "forestry"; + public static final String WOOD_AND_CARBON_DIR = getProperty("WOOD_AND_CARBON_DIR"); + //public static final String CARBON_FLUX_FILE = FOREST_DIR + File.separator + "carbon_flux_"; + public static final String WOOD_YIELD_FRST_TO_AGRI_FILENAME = "wood_yield_forest_to_agri.dat"; + public static final String WOOD_YIELD_FRST_TO_FRST_FILENAME = "wood_yield_forest_to_forest.dat"; + public static final String WOOD_YIELD_NTRL_TO_AGRI_FILENAME = "wood_yield_ntrl_to_agri.dat"; + public static final String WOOD_YIELD_NTRL_TO_FRST_FILENAME = "wood_yield_ntrl_to_forest.dat"; + public static final String CARBON_LUC_FILENAME = "carbon_luc.out"; + public static final String CARBON_NEE_FILENAME = "carbon_nee.out"; + public static final String NATURAL_FOREST_GROWTH_FILENAME = "growth_natural.out"; + 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); + + // Output + public static final String LAND_COVER_OUTPUT_FILE = OUTPUT_DIR + File.separator + "lc.txt"; + public static final String PRICES_OUTPUT_FILE = OUTPUT_DIR + File.separator + "prices.txt"; + public static final String DEMAND_OUTPUT_FILE = OUTPUT_DIR + File.separator + "demand.txt"; + public static final String DOMESTIC_OUTPUT_FILE = OUTPUT_DIR + File.separator + "domestic.txt"; + public static final String COUNTRY_DEMAND_FILE = OUTPUT_DIR + File.separator + "countryDemand.txt"; + public static final String DEMAND_OPTIMISATION_OUTPUT_FILE = OUTPUT_DIR + File.separator + "countryDemandOpt.txt"; + public static final String FOOD_BALANCE_SHEET_FILE = OUTPUT_DIR + File.separator + "fbs.txt"; + public static final String ANIMAL_NUMBERS_OUTPUT_FILE = OUTPUT_DIR + File.separator + "animals.txt"; + public static final String WOOD_CARBON_OUTPUT_FILE = OUTPUT_DIR + File.separator + "woodCarbon.txt"; + + public static final boolean OUTPUT_FOR_LPJG = getBooleanProperty("OUTPUT_FOR_LPJG", true); + public static final boolean INTERPOLATE_OUTPUT_YEARS = getBooleanProperty("INTERPOLATE_OUTPUT_YEARS", true); + + // Calibration related stuff + public static final boolean IS_CALIBRATION_RUN = getBooleanProperty("IS_CALIBRATION_RUN", false); + public static final String CALIB_DIR = IS_CALIBRATION_RUN ? OUTPUT_DIR : getProperty("CALIB_DIR", OUTPUT_DIR); + public static final int END_FIRST_STAGE_CALIBRATION = getIntProperty("END_FIRST_STAGE_CALIBRATION", 10); + public static final String SERIALIZED_LAND_USE_FILENAME = "landUseRaster.ser"; + public static final String SERIALIZED_CROP_USAGE_FILENAME = "countryCropUsages.ser"; + public static final String SERIALIZED_INTERNATIONAL_MARKET_FILENAME = "internationalMarket.ser"; + public static final String SERIALIZED_WOOD_USAGE_FILENAME = "countryWoodUsage.ser"; + public static final String SERIALIZED_LAND_COVER_FILENAME = "landCoverData.dat"; + public static final String SERIALIZED_LAND_USE_FILE = CALIB_DIR + File.separator + SERIALIZED_LAND_USE_FILENAME; + public static final String SERIALIZED_CROP_USAGE_FILE = CALIB_DIR + File.separator + SERIALIZED_CROP_USAGE_FILENAME; + public static final String SERIALIZED_INTERNATIONAL_MARKET_FILE = CALIB_DIR + File.separator + SERIALIZED_INTERNATIONAL_MARKET_FILENAME; + public static final String SERIALIZED_LAND_COVER_FILE = CALIB_DIR + File.separator + SERIALIZED_LAND_COVER_FILENAME; + public static final String SERIALIZED_WOOD_USAGE_FILE = CALIB_DIR + File.separator + SERIALIZED_WOOD_USAGE_FILENAME; + public static final String CHECKPOINT_LAND_USE_FILE = getProperty("CHECKPOINT_LAND_USE_FILE", OUTPUT_DIR + File.separator + SERIALIZED_LAND_USE_FILENAME); + public static final String CHECKPOINT_CROP_USAGE_FILE = getProperty("CHECKPOINT_CROP_USAGE_FILE", OUTPUT_DIR + File.separator + SERIALIZED_CROP_USAGE_FILENAME); + 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 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); + public static final String CLUSTERED_YIELD_FILE = getProperty("CLUSTERED_YIELD_FILE", CALIB_DIR + File.separator + "cluster.asc"); + public static final boolean GENERATE_NEW_YIELD_CLUSTERS = getBooleanProperty("GENERATE_NEW_YIELD_CLUSTERS", IS_CALIBRATION_RUN); + + + // Temporal configuration + public static final int START_TIMESTEP = getIntProperty("START_TIMESTEP", 0); + public static final int END_TIMESTEP = getIntProperty("END_TIMESTEP", 90); + public static final int TIMESTEP_SIZE = getIntProperty("TIMESTEP_SIZE", 1); + public static final int BASE_YEAR = getIntProperty("BASE_YEAR", 2010); + + // Import export limits + public static final double ANNUAL_MAX_IMPORT_CHANGE = getDoubleProperty("ANNUAL_MAX_IMPORT_CHANGE", 0.05); + public static final double MAX_IMPORT_CHANGE = getDoubleProperty("MAX_IMPORT_CHANGE", ANNUAL_MAX_IMPORT_CHANGE*TIMESTEP_SIZE); + + // Fertiliser application rates in kg/ha + public static final double MIN_FERT_AMOUNT = getDoubleProperty("MIN_FERT_AMOUNT", 0.0); + public static final double MID_FERT_AMOUNT = getDoubleProperty("MID_FERT_AMOUNT", 200.0); + public static final double MAX_FERT_AMOUNT = getDoubleProperty("MAX_FERT_AMOUNT", 1000.0); + public static final int FERT_AMOUNT_PADDING = getIntProperty("FERT_AMOUNT_PADDING", 4);; + + // SSP shift parameters + public static final double SSP_POPULATION_FACTOR = getDoubleProperty("SSP_POPULATION_FACTOR", 1.0); + public static final double SSP_GDP_PC_FACTOR = getDoubleProperty("SSP_GDP_PC_FACTOR", 1.0); + + // Other model parameters + public static final boolean CHANGE_DEMAND_YEAR = IS_CALIBRATION_RUN ? false : getBooleanProperty("CHANGE_DEMAND_YEAR", true); + public static final double DIETARY_CLOSURE = getDoubleProperty("DIETARY_CLOSURE", 0.25); // Amount diet converges in DIETARY_CLOSURE_GDP_CHANGE rate of GDP shift + public static final double DIETARY_CLOSURE_GDP_CHANGE = getDoubleProperty("DIETARY_CLOSURE_GDP_CHANGE", 2.0); // 2 is double of GDP + public static final double DIETARY_CLOSURE_PARAM = getDoubleProperty("DIETARY_CLOSURE_PARAM", Math.log((1-DIETARY_CLOSURE))/DIETARY_CLOSURE_GDP_CHANGE); // Zero is no dietary closure, number specifies closure rate for changes in GDP per capita, e.g. Math.log(0.5)/2 + public static final String SSP_SCENARIO = getProperty("SSP_SCENARIO", "SSP1_v9_130325"); + public static final ModelFitType DEMAND_ANIMAL_PROD_FIT = ModelFitType.findByName(getProperty("DEMAND_ANIMAL_PROD_FIT", "loglinear")); + public static final ModelFitType DEMAND_NON_ANIMAL_PROD_FIT = ModelFitType.findByName(getProperty("DEMAND_NON_ANIMAL_PROD_FIT", "loglinear")); + public static final boolean LIMIT_DEMAND_FRACTION = getBooleanProperty("LIMIT_DEMAND_FRACTION", true); + public static final boolean DEMAND_FRACT_BY_COST = getBooleanProperty("DEMAND_FRACT_BY_COST", false);; + public static final double MAX_INCOME_PROPORTION_FOOD_SPEND = getDoubleProperty("MAX_INCOME_PROPORTION_FOOD_SPEND", 0.7); + + public static final double PASTURE_HARVEST_FRACTION = getDoubleProperty("PASTURE_HARVEST_FRACTION", 0.5); + public static final double MEAT_EFFICIENCY = getDoubleProperty("MEAT_EFFICIENCY", 1.0); // 'meat' is includes feed conversion ratio already, this is tech. change or similar + public static final double IRRIGIATION_EFFICIENCY = getDoubleProperty("IRRIGIATION_EFFICIENCY", 0.5); + public static final int ELLIOTT_BASEYEAR = 2010; + public static final double ENVIRONMENTAL_WATER_CONSTRAINT = getDoubleProperty("ENVIRONMENTAL_WATER_CONSTRAINT", 0.5); // change with care, as due to normalisation it might not have the impact you first imagine + public static final double OTHER_WATER_USE_FACTOR = getDoubleProperty("OTHER_WATER_USE_FACTOR", 1.0); + public static final boolean USE_BLUE_WATER_FILE_IRRIG_CONSTRAINT = getBooleanProperty("USE_BLUE_WATER_FILE_IRRIG_CONSTRAINT", false);; + + public static final double LAND_CHANGE_COST = getDoubleProperty("LAND_CHANGE_COST", 0.2); + public static final double CROP_TO_PASTURE_COST_FACTOR = getDoubleProperty("CROP_TO_PASTURE_COST_FACTOR", 1.0); + public static final double AGRI_LAND_EXPANSION_COST_FACTOR = getDoubleProperty("AGRI_LAND_EXPANSION_COST_FACTOR", 1.0); + + public static final double CROP_INCREASE_COST = getDoubleProperty("CROP_INCREASE_COST", 0.05 * LAND_CHANGE_COST * AGRI_LAND_EXPANSION_COST_FACTOR); + 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 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); + + public static final boolean USE_BIOENERGY_TRAJECTORY = getBooleanProperty("USE_BIOENERGY_TRAJECTORY", true); // false is the old style, i.e. BIOENERGY_CHANGE_ANNUAL_RATE, BIOENERGY_CHANGE_START_YEAR and BIOENERGY_CHANGE_END_YEAR + // original way to specify changes in bioenergy + public static final double BIOENERGY_CHANGE_ANNUAL_RATE = IS_CALIBRATION_RUN ? 0.0 : getDoubleProperty("BIOENERGY_CHANGE_ANNUAL_RATE", 0.10); // 6.2/2.31/20 + public static final int BIOENERGY_CHANGE_START_YEAR = getIntProperty("BIOENERGY_CHANGE_START_YEAR", 2010); + public static final int BIOENERGY_CHANGE_END_YEAR = getIntProperty("BIOENERGY_CHANGE_END_YEAR", 2060); + // newer way + public static final boolean ENABLE_GEN2_BIOENERGY = getBooleanProperty("ENABLE_GEN2_BIOENERGY", true); + public static final String BIOENERGY_DEMAND_SCENARIO = getProperty("BIOENERGY_DEMAND_SCENARIO", "SSP2_RCP45"); + public static final String GEN2_BIOENERGY_MODEL = getProperty("GEN2_BIOENERGY_MODEL", "ENSEMBLE-MEAN"); + public static final double BIOENERGY_DEMAND_SHIFT = IS_CALIBRATION_RUN ? 1.0 : getDoubleProperty("BIOENERGY_DEMAND_SHIFT", 1.0); +// public static final double BIOENERGY_HEATING_VALUE_GJ_PER_T = getDoubleProperty("BIOENERGY_HEATING_VALUE_GJ_PER_T", 17.5); // GJ per t DM + + //Dietary stuff + public static final double RUMINANT_CHANGE_ANNUAL_RATE = getDoubleProperty("RUMINANT_CHANGE_ANNUAL_RATE", 0.0); + public static final double MONOGASTRIC_CHANGE_ANNUAL_RATE = getDoubleProperty("MONOGASTRIC_CHANGE_ANNUAL_RATE", 0.00); + public static final boolean CONSTANT_DIET_LOW_INCOME = getBooleanProperty("CONSTANT_DIET_LOW_INCOME", false); + public static final boolean CONSTANT_DIET_HIGH_INCOME = getBooleanProperty("CONSTANT_DIET_HIGH_INCOME", false); + //below should all sum to zero + public static final double CEREALS_SUB_PROPORTION = getDoubleProperty("CEREALS_SUB_PROPORTION", 0.3); + public static final double OILCROPSPULSES_SUB_PROPORTION = getDoubleProperty("OILCROPSPULSES_SUB_PROPORTION", 0.25); + public static final double STARCHY_ROOTS_SUB_PROPORTION = getDoubleProperty("STARCHY_ROOTS_SUB_PROPORTION", 0.25); + public static final double FRUITVEG_SUB_PROPORTION = getDoubleProperty("FRUITVEG_SUB_PROPORTION", 0.1); + public static final double SUGAR_SUB_PROPORTION = getDoubleProperty("SUGAR_SUB_PROPORTION", 0.1); + + public static final double MARKET_LAMBA = getDoubleProperty("MARKET_LAMBA", 0.4); // controls international market price adjustment rate + public static final boolean PRICE_UPDATE_BY_MARKET_IMBALANCE = getBooleanProperty("PRICE_UPDATE_BY_MARKET_IMBALANCE", false);; + public static final double MAX_PRICE_INCREASE = getDoubleProperty("MAX_PRICE_INCREASE", 1.5); + public static final double MAX_PRICE_DECREASE = getDoubleProperty("MAX_PRICE_DECREASE", .75); + public static final int DEMAND_RECALC_MAX_ITERATIONS = IS_CALIBRATION_RUN ? 0 : getIntProperty("DEMAND_RECALC_MAX_ITERATIONS", 1); // 0 is original behaviour + public static final boolean DEMAND_RECALC_ON_NEGATIVE_STOCK = IS_CALIBRATION_RUN ? false : getBooleanProperty("DEMAND_RECALC_ON_NEGATIVE_STOCK", false); + + public static final double POPULATION_AGGREG_LIMIT = getDoubleProperty("POPULATION_AGGREG_LIMIT", 30.0); // in millions, smaller countries are aggregated on a regional basis + public static final boolean PREDEFINED_COUNTRY_GROUPING = getBooleanProperty("PREDEFINED_COUNTRY_GROUPING", true); + + // 1536.7 Mha is all cropland in LUH2, or 1555.6 Mha from FAO. country_data[Country =="World" & Year == 2011, list(arable+ perm_crops)] + // 1377.6 Mha is area of crops in FAO crop production. con_prod_unadj[Country =="World" & Item %in% itemDetails[produced==TRUE]$Item & Year == 2011, sum(prod_area, na.rm=TRUE)] + // 1105.6 Mha is harvested in crops we represent. crop_prod[Country =="World" & Item %in% itemGroupMapping$cropProdItem & Year == 2011, sum(area)] + // extract values by running AltLURead.R and common.R + // So we do not represent crops covering (1341.911-1239.03)/1547.464=6.65% of cropland. Additionally 10.3% of cropland is set aside, fallow or failed crops, + public static final double UNHANDLED_CROP_RATE = getDoubleProperty("UNHANDLED_CROP_RATE", 0.0665); // mostly forage crops + public static final double SETASIDE_RATE = getDoubleProperty("SETASIDE_RATE", 0.103); // includes aside, fallow and failed cropland areas + + 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.0003); + 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", 1.4); // $500/t, 18% N/t + public static final double FERTILISER_MAX_COST = FERTILISER_COST_PER_T * MAX_FERT_AMOUNT/1000; + + public static final double DOMESTIC_PRICE_MARKUP = getDoubleProperty("DOMESTIC_PRICE_MARKUP", 1.0); + public static final double TRANSPORT_LOSSES = getDoubleProperty("TRANSPORT_LOSSES", 0.05); // in international trade + public static final double TRANSPORT_COST = getDoubleProperty("TRANSPORT_COST", 0.1); // 10% transport cost + public static final double TRADE_BARRIER_FACTOR_DEFAULT = getDoubleProperty("TRADE_BARRIER_FACTOR_DEFAULT", 0.2); // price factor in international trade, transport cost and real trade barriers + public static final double TRADE_BARRIER_MULTIPLIER = getDoubleProperty("TRADE_BARRIER_MULTIPLIER", 1.0); + public static final boolean ACTIVE_TRADE_BARRIERS = getBooleanProperty("ACTIVE_TRADE_BARRIERS", true); // if set to true read in barrier information from file, otherwise use default as above + public static final double TRADE_BARRIER_ENERGY_CROPS = getDoubleProperty("TRADE_BARRIER_ENERGY_CROPS", 0.01); // price factor in international trade, transport cost and real trade barriers + public static final boolean SHOCKS_POSSIBLE = getBooleanProperty("SHOCKS_POSSIBLE", false); + public static final double YIELD_SHOCK_MAGNIFIER = getDoubleProperty("YIELD_SHOCK_MAGNIFIER", 1.0); + + public static final boolean PROTECTED_AREAS_ENABLED = getBooleanProperty("PROTECTED_AREAS_ENABLED", true); + public static final double MIN_NATURAL_RATE = getDoubleProperty("MIN_NATURAL_RATE", 0.05); + public static final double MAX_CHINA_LAND_EXPANSION_RATE = getDoubleProperty("MAX_CHINA_LAND_EXPANSION_RATE", 0.011*0.4); // 1.1% max forest change 40% of natural land is forest + + public static final boolean DEBUG_JUST_DEMAND_OUTPUT = getBooleanProperty("DEBUG_JUST_DEMAND_OUTPUT", false); + public static final boolean DEBUG_LIMIT_COUNTRIES = getBooleanProperty("DEBUG_LIMIT_COUNTRIES", false); + public static final String DEBUG_COUNTRY_NAME = getProperty("DEBUG_COUNTRY_NAME", "United States of America"); + public static final String GAMS_COUNTRY_TO_SAVE = getProperty("GAMS_COUNTRY_TO_SAVE", "China");; + public static final boolean EXCLUDE_COUNTRIES_IN_LIST = getBooleanProperty("EXCLUDE_COUNTRIES_IN_LIST", false); + public static final String EXCLUDED_COUNTRIES_FILE = DATA_DIR + File.separator + "countries_excluded.csv"; + + public static final double PASTURE_MAX_IRRIGATION_RATE = getDoubleProperty("DEFAULT_MAX_IRRIGATION_RATE", 50.0); // shouldn't need this but some areas crops don't have a value, but was causing them to be selected + public static final int LPJG_TIMESTEP_SIZE = getIntProperty("LPJG_TIMESTEP_SIZE", 5); + public static final int LPJ_YEAR_OFFSET = getIntProperty("LPJ_YEAR_OFFSET", 0);; + + public static final int NUM_YIELD_CLUSTERS = getIntProperty("NUM_YIELD_CLUSTERS", 8000); + 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 + + // Protected areas forcing parameters + public static final boolean FORCE_PROTECTED_AREAS = IS_CALIBRATION_RUN ? false : getBooleanProperty("FORCE_PROTECTED_AREAS", false); + public static final int FORCE_PROTECTED_AREAS_START_YEAR = getIntProperty("FORCE_PROTECTED_AREAS_START_YEAR", 2020); + public static final int FORCE_PROTECTED_AREAS_END_YEAR = getIntProperty("FORCE_PROTECTED_AREAS_END_YEAR", 2040); + public static final double CONSTANT_PROTECTED_AREA_RATE = getDoubleProperty("CONSTANT_PROTECTED_AREA_RATE", Double.NaN); + public static final boolean HALFEARTH = getBooleanProperty("HALFEARTH", false); + + public static final boolean USE_CRAFTY_COUNTRIES = getBooleanProperty("USE_CRAFTY_COUNTRIES", false); + public static final String CRAFTY_PRODUCTION_DIR = getProperty("CRAFTY_PRODUCTION_DIR", OUTPUT_DIR + File.separator + "crafty"); + + public static final boolean EXTRAPOLATE_YIELD_FERT_RESPONSE = getBooleanProperty("EXTRAPOLATE_YIELD_FERT_RESPONSE", false); + + public static final boolean ADJUST_DIET_PREFS = getBooleanProperty("ADJUST_DIET_PREFS", false); + public static final int DIET_CHANGE_START_YEAR = getIntProperty("DIET_CHANGE_START_YEAR", 2020); + public static final int DIET_CHANGE_END_YEAR = getIntProperty("DIET_CHANGE_END_YEAR", 2040); + public static final String TARGET_DIET_FILE = getProperty("TARGET_DIET_FILE", DATA_DIR + File.separator + "TargetDiet.txt"); + public static final boolean APPLY_EXPORT_TAXES = getBooleanProperty("APPLY_EXPORT_TAXES", false); + public static final double EXPORT_TAX_RATE = getDoubleProperty("EXPORT_TAX_RATE", 1.0); + public static final double EXPORT_TAX_THRESHOLD = getDoubleProperty("EXPORT_TAX_THRESHOLD", 0.1); + + // 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 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 double INIT_WOOD_STOCK = getDoubleProperty("INIT_WOOD_STOCK", 1000.0); // tC-eq + public static final double ROUNDWOOD_DEMAND_ELASTICITY = getDoubleProperty("ROUNDWOOD_DEMAND_ELASTICITY", -0.21956); + public static final double FUELWOOD_DEMAND_ELASTICITY = getDoubleProperty("FUELDWOOD_DEMAND_ELASTICITY", -0.22787); + public static final double FOREST_ESTABLISHMENT_COST = getDoubleProperty("FOREST_ESTABLISHMENT_COST", 2.0); // $1000/ha + 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 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 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.3) : 0.0; // $1000/tC-eq +} diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java index 89406182b07844433ddcc9c1e72a0bfaf1e214ff..83dc56064d0051ea3ae6b5cf5fa6112ce80da255 100644 --- a/src/ac/ed/lurg/ModelMain.java +++ b/src/ac/ed/lurg/ModelMain.java @@ -1,760 +1,809 @@ -package ac.ed.lurg; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import ac.ed.lurg.carbon.CarbonFluxRasterSet; -import ac.ed.lurg.carbon.CarbonFluxReader; -import ac.ed.lurg.country.AbstractCountryAgent; -import ac.ed.lurg.country.AnimalRateManager; -import ac.ed.lurg.country.CompositeCountry; -import ac.ed.lurg.country.CompositeCountryManager; -import ac.ed.lurg.country.CountryAgentManager; -import ac.ed.lurg.country.CountryBoundaryRaster; -import ac.ed.lurg.country.CountryBoundaryReader; -import ac.ed.lurg.country.CountryPrice; -import ac.ed.lurg.country.GlobalPrice; -import ac.ed.lurg.demand.AbstractDemandManager; -import ac.ed.lurg.demand.BaseConsumpManager; -import ac.ed.lurg.demand.CalorieManager; -import ac.ed.lurg.demand.DemandManagerFromFile; -import ac.ed.lurg.demand.DemandManagerSSP; -import ac.ed.lurg.demand.ElasticDemandManager; -import ac.ed.lurg.forestry.WoodYieldRasterSet; -import ac.ed.lurg.landuse.ConversionCostReader; -import ac.ed.lurg.landuse.CropUsageData; -import ac.ed.lurg.landuse.CropUsageReader; -import ac.ed.lurg.landuse.FPUManager; -import ac.ed.lurg.landuse.InitProtectedAreasReader; -import ac.ed.lurg.landuse.IrrigationConstraintReader; -import ac.ed.lurg.landuse.IrrigationItem; -import ac.ed.lurg.landuse.IrrigationMaxAmountReader; -import ac.ed.lurg.landuse.IrrigationRasterSet; -import ac.ed.lurg.landuse.IrrigiationCostReader; -import ac.ed.lurg.landuse.LccKey; -import ac.ed.lurg.landuse.LandCoverItem; -import ac.ed.lurg.landuse.LandCoverReader; -import ac.ed.lurg.landuse.LandCoverTile; -import ac.ed.lurg.landuse.LandUseItem; -import ac.ed.lurg.landuse.LandTileReader; -import ac.ed.lurg.landuse.LandUseBinarySerializer; -import ac.ed.lurg.landuse.MaxCropAreaReader; -import ac.ed.lurg.landuse.ProtectedAreasReader; -import ac.ed.lurg.landuse.RunOffReader; -import ac.ed.lurg.landuse.WoodUsageData; -import ac.ed.lurg.landuse.WoodUsageReader; -import ac.ed.lurg.forestry.WoodYieldReader; -import ac.ed.lurg.output.LandUseOutputer; -import ac.ed.lurg.output.LpjgOutputer; -import ac.ed.lurg.types.CommodityType; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.WoodType; -import ac.ed.lurg.utils.FileWriterHelper; -import ac.ed.lurg.utils.LogWriter; -import ac.ed.lurg.yield.LPJYieldResponseMapReader; -import ac.ed.lurg.yield.YieldRaster; -import ac.sac.raster.IntegerRasterItem; -import ac.sac.raster.IntegerRasterReader; -import ac.sac.raster.RasterHeaderDetails; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterOutputer; -import ac.sac.raster.RasterSet; - -public class ModelMain { - - private CountryAgentManager countryAgents; - private CountryBoundaryRaster countryBoundaryRaster; - private AbstractDemandManager demandManager; - private AnimalRateManager animalRateManager; - private CompositeCountryManager compositeCountryManager; - LPJYieldResponseMapReader lpjYieldReader; - private RasterHeaderDetails desiredProjection; - - private InternationalMarket internationalMarket; - private IrrigationRasterSet currentIrrigationData; - private RasterSet<LandUseItem> globalLandUseRaster; - private RasterSet<IntegerRasterItem> clusterIdRaster; - private WoodYieldReader woodYieldReader; - private CarbonFluxReader carbonFluxReader; - - - public static void main(String[] args) { - ModelMain theModel = new ModelMain(); - System.out.println("Working Directory = " + System.getProperty("user.dir")); - theModel.setup(); - theModel.run(); - } - - /* setup models, reading inputs, etc. */ - private void setup() { - desiredProjection = RasterHeaderDetails.getGlobalHeaderFromCellSize(ModelConfig.CELL_SIZE_X, ModelConfig.CELL_SIZE_Y, "999"); - - BaseConsumpManager baseConsumpManager = new BaseConsumpManager(); - CalorieManager calorieManager = new CalorieManager(); - compositeCountryManager = new CompositeCountryManager(baseConsumpManager); - lpjYieldReader = new LPJYieldResponseMapReader(desiredProjection); - animalRateManager = new AnimalRateManager(compositeCountryManager); - - if (ModelConfig.DEMAND_FROM_FILE) - demandManager = new DemandManagerFromFile(compositeCountryManager,calorieManager); - else if (ModelConfig.PRICE_ELASTIC_DEMAND) - demandManager = new ElasticDemandManager(ModelConfig.SSP_SCENARIO, baseConsumpManager,calorieManager, compositeCountryManager); - else - demandManager = new DemandManagerSSP(ModelConfig.SSP_SCENARIO, baseConsumpManager,calorieManager, compositeCountryManager); - - currentIrrigationData = getFixedIrrigationData(); - countryBoundaryRaster = getCountryBoundaryRaster(); - clusterIdRaster = ModelConfig.GENERATE_NEW_YIELD_CLUSTERS ? new RasterSet<IntegerRasterItem>(desiredProjection) : getClusterRaster(); - - globalLandUseRaster = new RasterSet<LandUseItem>(desiredProjection); - internationalMarket = new InternationalMarket(); - - woodYieldReader = new WoodYieldReader(desiredProjection); - carbonFluxReader = new CarbonFluxReader(desiredProjection); - - createCountryAgents(compositeCountryManager.getAll()); - } - - /* run the model */ - private void run() { - for (int i = ModelConfig.START_TIMESTEP; i <= ModelConfig.END_TIMESTEP; i++) { - Timestep timestep = new Timestep(i); - try { - doTimestep(timestep); - } catch (Exception e) { - LpjgOutputer.writeMarkerFile(timestep.getYear(), true); - throw new RuntimeException(e); - } - } - } - - private void doTimestep(Timestep timestep) { - System.gc(); - LogWriter.println("Timestep: " + timestep.toString()); - - YieldRaster yieldSurfaces = getYieldSurfaces(timestep); // this will wait for the marker file from LPJ if configured to do so - getUpdateIrrigationData(timestep, yieldSurfaces); // updating currentIrrigationData - - // When running half earth we can to alter protected areas data at a point in time - if(ModelConfig.HALFEARTH && ModelConfig.FORCE_PROTECTED_AREAS_START_YEAR == timestep.getYear() && !ModelConfig.IS_CALIBRATION_RUN) { - new ProtectedAreasReader(globalLandUseRaster).getRasterDataFromFile(ModelConfig.HALF_EARTH_FILE); - countryAgents.updateProtectedAreasForAll(globalLandUseRaster); - } - - double previousCarbonDemand = (timestep.isInitialTimestep() || ModelConfig.IS_CALIBRATION_RUN ) ? 0: demandManager.getCarbonDemand(timestep.getPreviousTimestep()); - double carbonDemand = demandManager.getCarbonDemand(ModelConfig.IS_CALIBRATION_RUN ? new Timestep(1) : timestep); - double carbonDemandIncrease = (carbonDemand > previousCarbonDemand) ? carbonDemand - previousCarbonDemand: 0; - - WoodYieldRasterSet woodYieldData = getWoodYieldData(timestep); - CarbonFluxRasterSet carbonFluxData = getCarbonFluxData(timestep); - - Map<LccKey, Double> conversionCosts; - conversionCosts = (ModelConfig.CONVERSION_COSTS_FROM_FILE) ? new ConversionCostReader().read() : new ConversionCostReader().calcFromConfig(); - - countryAgents.determineProductionForAll(timestep, yieldSurfaces, currentIrrigationData, carbonFluxData, woodYieldData, - conversionCosts, carbonDemandIncrease); - internationalMarket.determineInternationalTrade(countryAgents.getAll(), carbonDemand, timestep); - - int i = 0; - while (i < ModelConfig.DEMAND_RECALC_MAX_ITERATIONS || - (ModelConfig.DEMAND_RECALC_ON_NEGATIVE_STOCK && internationalMarket.negativeStockLevelsExist() && i<10)) { // loop if negative stock have we haven't tried 10 times already - LogWriter.println("\n++ Re-estimating prices and demand: timestep " + timestep.getTimestep() + ": interation " + i); - countryAgents.recalculateDemandAndNetImportsForAll(); // recalculate demand from new prices and calculate imports and exports - internationalMarket.determineInternationalTrade(countryAgents.getAll(), carbonDemand, timestep); // calculate prices - i++; - } - internationalMarket.applyPriceShocks(timestep); - - // output results - outputTimestepResults(timestep); - checkAndSaveCheckpoint(timestep); - - } - - private void checkAndSaveCheckpoint(Timestep timestep) { - if (ModelConfig.IS_CALIBRATION_RUN || shouldSaveCheckpoint(timestep)) - serializeCheckpoint(); - } - - private boolean shouldSaveCheckpoint(Timestep timestep) { - if (ModelConfig.CHECKPOINT_YEARS != null) { - LogWriter.println("Looking to see if checkpoint year reached " + ModelConfig.CHECKPOINT_YEARS); - - String[] yearStr = ModelConfig.CHECKPOINT_YEARS.split(","); - for(int i=0; i<yearStr.length; i++) { - LogWriter.println("Got a checkpoint yearStr " + yearStr[i]); - int year = Integer.parseInt(yearStr[i]); - if (timestep.getYear() == year) - return true; - } - } - return false; - } - - private void writeLandCoverFile(Timestep timestep, RasterSet<LandUseItem> landUseRaster) { - try { - StringBuffer sbHeadings = new StringBuffer("Year,Cropland,Pasture,ManForest,UnmanForest,Natural,AbPasture,Suitable,EnergyCrop,FertCrop,IrrigCrop"); - BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.LAND_COVER_OUTPUT_FILE, sbHeadings.toString()); - - StringBuffer sbData = new StringBuffer(); - sbData.append(String.format("%d,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f", timestep.getYear(), - LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.CROPLAND), - LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.PASTURE), - LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.TIMBER_FOREST), - LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.CARBON_FOREST), - LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.NATURAL), - LandUseItem.getAbandonedPasture(landUseRaster.values()), - LandUseItem.getSuitableTotal(landUseRaster.values(), timestep.getYear())) - ); - - sbData.append(String.format(",%.1f", LandUseItem.getTotalCropArea(landUseRaster.values(), CropType.ENERGY_CROPS))); - sbData.append(String.format(",%.1f", LandUseItem.getFertiliserTotal(landUseRaster.values(), CropType.getCropsLessPasture()) / 1000)); - sbData.append(String.format(",%.1f", LandUseItem.getIrrigationTotal(landUseRaster.values(), CropType.getCropsLessPasture()))); - - outputFile.write(sbData.toString()); - outputFile.newLine(); - outputFile.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - - private void writeGlobalMarketFile(Timestep timestep) { - try { - BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.PRICES_OUTPUT_FILE, "Year,Crop,Imports (Mt),Exports (Mt),New export price, Stock Levels (Mt)"); - internationalMarket.writeGlobalMarketFile(timestep, outputFile); - outputFile.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - - private void writeGlobalFoodBalanceSheet(Timestep timestep, RasterSet<LandUseItem> landUseRaster) { - try { - BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.FOOD_BALANCE_SHEET_FILE, "Year,Crop,Production,Imports,Export,TransportLosses,StockVar,Supply,MonogastricsFeed,RuminantsFeed,SeedAndOtherLosses,FoodAnd1stGen,ProdArea"); - Map<CropType, GlobalPrice> worldPrices = internationalMarket.getWorldPrices(); - double harvestedAndFallowArea = 0; - - for (CropType crop : CropType.getCropsLessPasture()) { - GlobalPrice priceQuantity = worldPrices.get(crop); // some specific logic for import/exports and this has been aggregated already, so best to use it - double prod=0, prodArea=0, feedMonogastrics=0, feedRuminants=0; - double exportsBeforeTL=0, imports=0, transportloss=0, stockChange=0; - - if (priceQuantity != null) { - exportsBeforeTL = priceQuantity.getExportsBeforeTransportLoss(); - imports = priceQuantity.getImportAmount(); - transportloss = priceQuantity.getTransportLosses(); - stockChange = priceQuantity.getStockChange(); - } - - for (AbstractCountryAgent ca : countryAgents.getAll()) { - Map<CropType, CropUsageData> allCropUsage = ca.getCropUsageData(); - CropUsageData cropUsage = allCropUsage.get(crop); - if (cropUsage != null) { - prod += cropUsage.getProductionExpected(); - prodArea += cropUsage.getArea(); - feedMonogastrics += cropUsage.getMonogastricFeed(); - feedRuminants += cropUsage.getRuminantFeed(); - } - } - - double seedAndWaste = prod * crop.getSeedAndWasteRate(); - double netSupply = prod - exportsBeforeTL + imports; - double foodAnd1stGen = netSupply - feedMonogastrics - feedRuminants - seedAndWaste; - - if (!crop.equals(CropType.SETASIDE)) - prodArea *= (1-ModelConfig.UNHANDLED_CROP_RATE); // remove unhandled crop area - - harvestedAndFallowArea += prodArea; - - StringBuffer sbData = new StringBuffer(); - sbData.append(String.format("%d,%s", timestep.getYear(), crop.getGamsName())); - sbData.append(String.format(",%.2f", prod)); - sbData.append(String.format(",%.2f,%.2f,%.2f,%.2f", imports, exportsBeforeTL, transportloss, stockChange)); - sbData.append(String.format(",%.2f,%.2f,%.2f,%.2f,%.2f", netSupply, feedMonogastrics, feedRuminants, seedAndWaste, foodAnd1stGen)); - sbData.append(String.format(",%.2f", prodArea)); - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - - double cropLandArea = LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.CROPLAND); - double unhandledArea = cropLandArea - harvestedAndFallowArea; - outputFile.write(String.format("%d,%s,,,,,,,,,,,%.2f", timestep.getYear(), "unhandled", unhandledArea)); - outputFile.newLine(); - - outputFile.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - - private void writeDemandFile(Timestep timestep) { - try { - BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.DEMAND_OUTPUT_FILE, "Year,Commodity,Amount (Mt)"); - - for (CommodityType comm : CommodityType.getAllFoodItems()) { - double demandAmount = 0; - - for (AbstractCountryAgent country : countryAgents.getAll()) { - Map<CommodityType, Double> demands = country.getCurrentProjectedDemand(); - if (demands == null) { - LogWriter.printlnError(country.getCountry() + " " + comm); - } - - Double d = demands.get(comm); - if (d != null) { - demandAmount += d.doubleValue(); - LogWriter.println(String.format("%s,%s,%.4f", country.getCountry(), comm.getGamsName(), d)); - } - } - StringBuffer sbData = new StringBuffer(); - sbData.append(String.format("%d,%s", timestep.getYear(), comm.getGamsName())); - sbData.append(String.format(",%.1f", demandAmount)); - - LogWriter.println("Global demand " + timestep.getYear() + " " + comm.getGamsName() + " " + demandAmount + "\n"); - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - - double gen2EcDemand = countryAgents.getAll().stream().mapToDouble(c -> c.getCurrentGen2EcDemand()).sum(); - outputFile.write(String.format("%d,%s,%.1f", timestep.getYear(), CropType.ENERGY_CROPS.getGamsName(), gen2EcDemand)); - outputFile.newLine(); - - outputFile.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - - private void writeDomesticProductionFile(Timestep timestep) { - try { - StringBuffer sbHeadings = new StringBuffer("Year, Country, Crop, Area, Production, Production_cost, Import_price, Export_price, Consumer_price, Net_imports, Net_import_cost, Prod_shock, Rum_feed_amount, Mon_feed_amount"); - BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.DOMESTIC_OUTPUT_FILE, sbHeadings.toString()); - - for (CropType crop : CropType.getAllItems()) { - for (AbstractCountryAgent country : countryAgents.getAll()) { - - Map<CropType, CropUsageData> cropUsageAllCrops = country.getCropUsageData(); - CropUsageData cropUsage = cropUsageAllCrops.get(crop); - - if (cropUsage == null) - continue; - - double prodCosts = cropUsage.getTotalProdCost(); - double prod = cropUsage.getProductionExpected(); - double prodShock = cropUsage.getProductionShock(); - double area = cropUsage.getArea(); - double rumFeedAmount = cropUsage.getRuminantFeed(); - double monFeedAmount = cropUsage.getMonogastricFeed(); - - double importPrice = 0; - double exportPrice = 0 ; - double consumerPrice = 0; - double netImports = 0; - double netImportCost = 0; - - if (crop.isImportedCrop()) { - CountryPrice px = country.getCurrentCountryPrices().get(crop); - importPrice = px.getImportPrice(); - exportPrice = px.getExportPrice(); - consumerPrice = px.getConsumerPrice(); - netImports = cropUsage.getNetImportsExpected(); //this isn't accounting for transport losses in exports - netImportCost = cropUsage.getNetImportCostExpected(); - } - - StringBuffer sbData = new StringBuffer(); - sbData.append(String.format("%d,%s,%s", timestep.getYear(), country.getCountry(), crop.getGamsName())); - sbData.append(String.format(",%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f", area, prod, prodCosts, importPrice, exportPrice, consumerPrice, netImports, netImportCost, prodShock, rumFeedAmount, monFeedAmount)); - - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - } - outputFile.close(); - - } catch (IOException e) { - LogWriter.print(e); - } - } - - private void writeCountryDemandFile(Timestep timestep){ - - try { - StringBuffer sbHeadings = new StringBuffer("Year, Country, Commodity, Demand, BioenergyDemand"); - BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.COUNTRY_DEMAND_FILE, sbHeadings.toString()); - - for (AbstractCountryAgent country : countryAgents.getAll()) { - for (CommodityType commodity : CommodityType.getAllFoodItems()) { - double bioenergyDemand = demandManager.getFirstGenBioenergyDemand(country.getCountry(), timestep.getYear(), commodity); - double demand = country.getCurrentProjectedDemand().get(commodity); - - StringBuffer sbData = new StringBuffer(); - sbData.append(String.format("%d,%s,%s", timestep.getYear(), country.getCountry(), commodity.getGamsName())); - sbData.append(String.format(",%.3f,%.3f", demand, bioenergyDemand)); - - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - } - outputFile.close(); - - } catch (IOException e) { - LogWriter.print(e); - } - } - - private void writeAnimalNumber(Timestep timestep) { - try { - StringBuffer sbHeadings = new StringBuffer("Year,Country,FAOItem,Heads(M)"); - BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.ANIMAL_NUMBERS_OUTPUT_FILE, sbHeadings.toString()); - - for (AbstractCountryAgent country : countryAgents.getAll()) { - Map<CropType, CropUsageData> cropUsageAllCrops = country.getCropUsageData(); - for (CropType crop : CropType.getMeatTypes()) { - CropUsageData cropusage = cropUsageAllCrops.get(crop); - if (cropusage == null) - continue; - double prod = cropusage.getProductionExpected(); - - Map<String, Double> animalRates = animalRateManager.getAnimalRates(country.getCountry(), crop); - for (Entry<String, Double> entry : animalRates.entrySet()) { - StringBuffer sbData = new StringBuffer(); - double animalNum = prod * entry.getValue(); - sbData.append(String.format("%d,%s,%s,%.4f", timestep.getYear(), country.getCountry(), entry.getKey(), animalNum)); - outputFile.write(sbData.toString()); - outputFile.newLine(); - } - } - } - outputFile.close(); - - } catch (IOException e) { - LogWriter.print(e); - } - } - - private void outputTimestepResults(Timestep timestep) { - - writeLandCoverFile(timestep, globalLandUseRaster); - writeGlobalMarketFile(timestep); - writeDemandFile(timestep); - writeDomesticProductionFile(timestep); - writeCountryDemandFile(timestep); - writeGlobalFoodBalanceSheet(timestep, globalLandUseRaster); - writeAnimalNumber(timestep); - - if (ModelConfig.OUTPUT_FOR_LPJG) { - for (int outputYear : timestep.getYearsFromLast()) { - LogWriter.println("Outputing Year: " + outputYear); - RasterSet<LandUseItem> landUseToOutput = null; - - if (outputYear == timestep.getYear()) { - landUseToOutput = globalLandUseRaster; - } - else if (ModelConfig.INTERPOLATE_OUTPUT_YEARS) { - // we run with 1 year time steps these days so this code is redundant and flawed - /* InterpolatingRasterSet<LandUseItem> intermediateLandUse = new InterpolatingRasterSet<LandUseItem>( landUseRaster.getHeaderDetails()) { - private static final long serialVersionUID = 1306045141011047760L; - protected LandUseItem createRasterData() { - return new LandUseItem(); - } - }; - intermediateLandUse.setup(globalLandUseRaster, landUseRaster, timestep.getPreviousTimestep().getYear(), timestep.getYear(), outputYear); - landUseToOutput = intermediateLandUse; */ - } - - if (landUseToOutput != null) { - LpjgOutputer lpjOutputer = new LpjgOutputer(outputYear, landUseToOutput); - lpjOutputer.writeOutput(); - } - } - outputWaterAvailablity(timestep, currentIrrigationData); // uses the year directory structure created above - } - - if (timestep.isInitialTimestep() && ModelConfig.GENERATE_NEW_YIELD_CLUSTERS) - outputClusters(clusterIdRaster); - - // Output LandUses to tabular file, for analysis (perhaps) - LogWriter.println("Outputing land uses Year: " + timestep.getYear()); - LandUseOutputer landuseOutputer = new LandUseOutputer(timestep.getYear(), globalLandUseRaster); - landuseOutputer.writeOutput(); - - // don't really need this a LPJ outputs have same data, although in a slightly different format - // outputLandCover(timestep.getYear(), landUseRaster, LandCoverType.CROPLAND); - // outputLandCover(timestep.getYear(), landUseRaster, LandCoverType.PASTURE); - } - - - private void outputWaterAvailablity(Timestep timestep, IrrigationRasterSet irrigiationRS) { - new RasterOutputer<Double, IrrigationItem>(irrigiationRS, ModelConfig.OUTPUT_DIR + File.separator + timestep.getYear() + File.separator + "IrrigConstraint.asc") { - @Override - public Double getValue(RasterKey location) { - IrrigationItem item = results.get(location); - if (item == null) - return null; - - return item.getIrrigConstraint(); - } - }.writeOutput(); - } - - private void outputClusters(RasterSet<IntegerRasterItem> landUseRaster) { - new RasterOutputer<Integer, IntegerRasterItem>(landUseRaster, ModelConfig.CLUSTERED_YIELD_FILE) { - @Override - public Integer getValue(RasterKey location) { - IntegerRasterItem item = results.get(location); - if (item == null) - return null; - - return item.getInt(); - } - }.writeOutput(); - } - - public RasterSet<IntegerRasterItem> getClusterRaster() { - RasterSet<IntegerRasterItem> clusters = new RasterSet<IntegerRasterItem>(desiredProjection) { - private static final long serialVersionUID = 2467452274591854417L; - - @Override - protected IntegerRasterItem createRasterData() { - return new IntegerRasterItem(0); - } - }; - - IntegerRasterReader clusterReader = new IntegerRasterReader(clusters); - clusterReader.getRasterDataFromFile(ModelConfig.CLUSTERED_YIELD_FILE); - return clusters; - } - - public CountryBoundaryRaster getCountryBoundaryRaster() { - CountryBoundaryRaster countryBoundaries = new CountryBoundaryRaster(desiredProjection, compositeCountryManager); - CountryBoundaryReader countryReader = new CountryBoundaryReader(countryBoundaries); - countryReader.getRasterDataFromFile(ModelConfig.COUNTRY_BOUNDARY_FILE); - return countryBoundaries; - } - - public void createCountryAgents(Collection<CompositeCountry> countryGrouping) { - countryAgents = new CountryAgentManager(compositeCountryManager, demandManager, countryBoundaryRaster, internationalMarket, clusterIdRaster, globalLandUseRaster); - Map<CompositeCountry, Map<CropType, CropUsageData>> cropUsageDataMap = getInitialCropUsageData(); - Map<CompositeCountry, Map<WoodType, WoodUsageData>> woodUsageDataMap = getInitialWoodUsageData(); - RasterSet<LandUseItem> initLU = getInitialLandUse(); - - for (CompositeCountry cc : countryGrouping) { - countryAgents.addForCountry(cc, cropUsageDataMap, initLU, woodUsageDataMap); - globalLandUseRaster.putAll(initLU); - } - } - - private RasterSet<LandUseItem> getInitialLandUse() { - RasterSet<LandUseItem> initialLU; - if (ModelConfig.IS_CALIBRATION_RUN) - initialLU = getLandUseFromBaseline(); - else - initialLU = deserializeLandUse(); - - return initialLU; - } - - private Map<CompositeCountry, Map<CropType, CropUsageData>> getInitialCropUsageData() { - Map<CompositeCountry, Map<CropType, CropUsageData>> cropUsageDataMap; - if (ModelConfig.IS_CALIBRATION_RUN) - cropUsageDataMap = new CropUsageReader(compositeCountryManager).getCommodityData(); - else - cropUsageDataMap = deserializeCropUsage(); - - return cropUsageDataMap; - } - - private Map<CompositeCountry, Map<WoodType, WoodUsageData>> getInitialWoodUsageData() { - Map<CompositeCountry, Map<WoodType, WoodUsageData>> woodUsageDataMap; - if (ModelConfig.IS_CALIBRATION_RUN) { - woodUsageDataMap = new WoodUsageReader(compositeCountryManager).getWoodUsageData(); - - } else { - woodUsageDataMap = deserializeWoodUsage(); - } - return woodUsageDataMap; - } - - @SuppressWarnings("unchecked") - private Map<CompositeCountry, Map<WoodType, WoodUsageData>> deserializeWoodUsage() { - try { - Map<CompositeCountry, Map<WoodType, WoodUsageData>> woodUsageDataMap; - FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_WOOD_USAGE_FILE); - ObjectInputStream in = new ObjectInputStream(fileIn); - woodUsageDataMap = (Map<CompositeCountry, Map<WoodType, WoodUsageData>>) in.readObject(); - in.close(); - fileIn.close(); - LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_WOOD_USAGE_FILE); - return woodUsageDataMap; - } catch (IOException i) { - LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_WOOD_USAGE_FILE); - LogWriter.print(i); - return null; - } catch (ClassNotFoundException c) { - LogWriter.printlnError("Map<CompositeCountry, Double[]> not found"); - c.printStackTrace(); - return null; - } - } - - private void serializeLandUse(RasterSet<LandUseItem> landUseRaster) { - RasterSet<LandUseItem> rasterToSerialise = new RasterSet<LandUseItem>(desiredProjection); - for (Map.Entry<RasterKey, LandUseItem> entry : landUseRaster.entrySet()) { - LandUseItem newLuItem = new LandUseItem(entry.getValue()); - newLuItem.overwriteLandCoverAreas(new HashMap<LandCoverType, LandCoverTile>()); // Clear data - rasterToSerialise.put(entry.getKey(), newLuItem); - } - try { - LogWriter.println("Starting serializing LandUse to " + ModelConfig.SERIALIZED_LAND_USE_FILE); - FileOutputStream fileOut = new FileOutputStream(ModelConfig.SERIALIZED_LAND_USE_FILE); - ObjectOutputStream out = new ObjectOutputStream(fileOut); - out.writeObject(rasterToSerialise); - out.close(); - fileOut.close(); - LogWriter.println("Serialized data is saved"); - } catch (IOException i) { - i.printStackTrace(); - } - - LogWriter.println("Starting serializing LandCover to " + ModelConfig.SERIALIZED_LAND_COVER_FILE); - LandUseBinarySerializer luSer = new LandUseBinarySerializer(); - luSer.serializeLandUse(globalLandUseRaster); - LogWriter.println("LandCover data is saved"); - } - - @SuppressWarnings("unchecked") - private RasterSet<LandUseItem> deserializeLandUse() { - try { - RasterSet<LandUseItem> initLU; - FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_LAND_USE_FILE); - ObjectInputStream in = new ObjectInputStream(fileIn); - initLU = (RasterSet<LandUseItem>) in.readObject(); - in.close(); - fileIn.close(); - LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_LAND_USE_FILE); - LandUseBinarySerializer luSer = new LandUseBinarySerializer(); - luSer.deserializeLandUse(initLU); - return initLU; - } catch (IOException i) { - LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_LAND_USE_FILE); - LogWriter.print(i); - return null; - } catch (ClassNotFoundException c) { - LogWriter.printlnError("RasterSet<LandUseItem> class not found"); - c.printStackTrace(); - return null; - } - } - - @SuppressWarnings("unchecked") - private Map<CompositeCountry, Map<CropType, CropUsageData>> deserializeCropUsage() { - try { - Map<CompositeCountry, Map<CropType, CropUsageData>> initCropUsage; - FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_CROP_USAGE_FILE); - ObjectInputStream in = new ObjectInputStream(fileIn); - initCropUsage = (Map<CompositeCountry, Map<CropType, CropUsageData>>) in.readObject(); - in.close(); - fileIn.close(); - LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_CROP_USAGE_FILE); - return initCropUsage; - } catch (IOException i) { - LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_CROP_USAGE_FILE); - LogWriter.print(i); - return null; - } catch (ClassNotFoundException c) { - LogWriter.printlnError("Map<CompositeCountry, Map<CropType, CropUsageData>> not found"); - c.printStackTrace(); - return null; - } - } - - - /** this is if we are starting from Hurtt of other initial land covers (so we don't have land uses and intensity data) */ - private RasterSet<LandUseItem> getLandUseFromBaseline() { - RasterSet<LandCoverItem> initialLC = new RasterSet<LandCoverItem>(desiredProjection) { - private static final long serialVersionUID = 4642550777741425501L; - - protected LandCoverItem createRasterData() { - return new LandCoverItem(); - } - }; - - new MaxCropAreaReader(initialLC).getRasterDataFromFile(ModelConfig.HIGH_SLOPE_AREAS_FILE); // Fraction unavailable for conversion - new LandCoverReader(initialLC).getRasterDataFromFile(ModelConfig.INITAL_LAND_COVER_FILE); // Land cover fractions - new LandTileReader(initialLC).getRasterDataFromFile(ModelConfig.LAND_COVER_AGE_DIST_FILENAME); // Age distribution of land cover - new InitProtectedAreasReader(initialLC).getRasterDataFromFile(ModelConfig.PROTECTED_AREAS_FILE); // Protected fraction - - RasterSet<LandUseItem> landUseRaster = new RasterSet<LandUseItem>(initialLC.getHeaderDetails()); - - for (Map.Entry<RasterKey, LandCoverItem> entry : initialLC.entrySet()) { - //LogWriter.println(initialLC.getXCoordin(entry.getKey()) + " " + initialLC.getYCoordin(entry.getKey())); - landUseRaster.put(entry.getKey(), new LandUseItem(entry.getValue())); - } - - return landUseRaster; - } - - private YieldRaster getYieldSurfaces(Timestep timestep) { - return lpjYieldReader.getRasterData(timestep); - } - - /** Get irrigation data that does not change with time, should only be called once */ - private IrrigationRasterSet getFixedIrrigationData() { - IrrigationRasterSet fixedIrrigData = new IrrigationRasterSet(desiredProjection, new FPUManager(desiredProjection)); - new IrrigiationCostReader(fixedIrrigData).getRasterDataFromFile(ModelConfig.IRRIGATION_COST_FILE); - new IrrigationConstraintReader(fixedIrrigData).getRasterDataFromFile(ModelConfig.IRRIGATION_CONSTRAINT_FILE); - - String baseTimestepRootDir = Timestep.getYearSubDir(ModelConfig.YIELD_DIR, ModelConfig.ELLIOTT_BASEYEAR); // needs to be Elliott base timestep - new RunOffReader(fixedIrrigData, true).getRasterDataFromFile(baseTimestepRootDir + File.separator + ModelConfig.IRRIG_RUNOFF_FILE); - - fixedIrrigData.calcIrrigConstraintOffsets(); // should have everything we need to calc offset between Elliott and LPJ data - return fixedIrrigData; - } - - /** Get carbon flux data */ - private CarbonFluxRasterSet getCarbonFluxData(Timestep timestep) { - if (ModelConfig.IS_CARBON_ON) { - return carbonFluxReader.getCarbonFluxes(globalLandUseRaster, timestep); - } else { - return new CarbonFluxRasterSet(desiredProjection); - } - } - - /** Get wood yield data */ - private WoodYieldRasterSet getWoodYieldData(Timestep timestep) { - if (ModelConfig.IS_FORESTRY_ON) { - return woodYieldReader.getWoodYields(globalLandUseRaster, timestep, internationalMarket.getWoodPrice().getExportPrice()); - } else { - return new WoodYieldRasterSet(desiredProjection); - } - } - - /** Ugly in situ update of currentIrrigationData, better if IrrigationRasterSets were handled more immutably */ - private void getUpdateIrrigationData(Timestep timestep, YieldRaster yieldSurfaces) { - String rootDir = timestep.getYearSubDir(ModelConfig.YIELD_DIR); - - new IrrigationMaxAmountReader(currentIrrigationData).getRasterDataFromFile(rootDir + File.separator + ModelConfig.IRRIG_MAX_WATER_FILENAME); - - if (!ModelConfig.USE_BLUE_WATER_FILE_IRRIG_CONSTRAINT) { - new RunOffReader(currentIrrigationData, false).getRasterDataFromFile(rootDir + File.separator + ModelConfig.IRRIG_RUNOFF_FILE); - currentIrrigationData.updateIrrigConstraints(timestep); - } - } - - private void serializeCheckpoint() { - serializeLandUse(globalLandUseRaster); - countryAgents.serializeCropUsageForAll(); - countryAgents.serializeWoodUsageForAll(); - internationalMarket.serializeGlobalPrices(); - } -} +package ac.ed.lurg; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import ac.ed.lurg.carbon.CarbonFluxRasterSet; +import ac.ed.lurg.carbon.CarbonFluxReader; +import ac.ed.lurg.country.AbstractCountryAgent; +import ac.ed.lurg.country.AnimalRateManager; +import ac.ed.lurg.country.CompositeCountry; +import ac.ed.lurg.country.CompositeCountryManager; +import ac.ed.lurg.country.CountryAgentManager; +import ac.ed.lurg.country.CountryBoundaryRaster; +import ac.ed.lurg.country.CountryBoundaryReader; +import ac.ed.lurg.country.CountryPrice; +import ac.ed.lurg.country.GlobalPrice; +import ac.ed.lurg.demand.AbstractDemandManager; +import ac.ed.lurg.demand.BaseConsumpManager; +import ac.ed.lurg.demand.CalorieManager; +import ac.ed.lurg.demand.DemandManagerFromFile; +import ac.ed.lurg.demand.DemandManagerSSP; +import ac.ed.lurg.demand.ElasticDemandManager; +import ac.ed.lurg.forestry.WoodYieldRasterSet; +import ac.ed.lurg.landuse.ConversionCostReader; +import ac.ed.lurg.landuse.CropUsageData; +import ac.ed.lurg.landuse.CropUsageReader; +import ac.ed.lurg.landuse.FPUManager; +import ac.ed.lurg.landuse.InitProtectedAreasReader; +import ac.ed.lurg.landuse.IrrigationConstraintReader; +import ac.ed.lurg.landuse.IrrigationItem; +import ac.ed.lurg.landuse.IrrigationMaxAmountReader; +import ac.ed.lurg.landuse.IrrigationRasterSet; +import ac.ed.lurg.landuse.IrrigiationCostReader; +import ac.ed.lurg.landuse.LccKey; +import ac.ed.lurg.landuse.LandCoverItem; +import ac.ed.lurg.landuse.LandCoverReader; +import ac.ed.lurg.landuse.LandCoverTile; +import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.LandTileReader; +import ac.ed.lurg.landuse.LandUseBinarySerializer; +import ac.ed.lurg.landuse.MaxCropAreaReader; +import ac.ed.lurg.landuse.ProtectedAreasReader; +import ac.ed.lurg.landuse.RunOffReader; +import ac.ed.lurg.landuse.WoodUsageData; +import ac.ed.lurg.landuse.WoodUsageReader; +import ac.ed.lurg.forestry.WoodYieldReader; +import ac.ed.lurg.output.LandUseOutputer; +import ac.ed.lurg.output.LpjgOutputer; +import ac.ed.lurg.types.CommodityType; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.WoodType; +import ac.ed.lurg.utils.FileWriterHelper; +import ac.ed.lurg.utils.LogWriter; +import ac.ed.lurg.yield.LPJYieldResponseMapReader; +import ac.ed.lurg.yield.YieldRaster; +import ac.sac.raster.IntegerRasterItem; +import ac.sac.raster.IntegerRasterReader; +import ac.sac.raster.RasterHeaderDetails; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterOutputer; +import ac.sac.raster.RasterSet; + +public class ModelMain { + + private CountryAgentManager countryAgents; + private CountryBoundaryRaster countryBoundaryRaster; + private AbstractDemandManager demandManager; + private AnimalRateManager animalRateManager; + private CompositeCountryManager compositeCountryManager; + LPJYieldResponseMapReader lpjYieldReader; + private RasterHeaderDetails desiredProjection; + + private InternationalMarket internationalMarket; + private IrrigationRasterSet currentIrrigationData; + private RasterSet<LandUseItem> globalLandUseRaster; + private RasterSet<IntegerRasterItem> clusterIdRaster; + private WoodYieldReader woodYieldReader; + private CarbonFluxReader carbonFluxReader; + + + public static void main(String[] args) { + ModelMain theModel = new ModelMain(); + System.out.println("Working Directory = " + System.getProperty("user.dir")); + theModel.setup(); + theModel.run(); + } + + /* setup models, reading inputs, etc. */ + private void setup() { + desiredProjection = RasterHeaderDetails.getGlobalHeaderFromCellSize(ModelConfig.CELL_SIZE_X, ModelConfig.CELL_SIZE_Y, "999"); + + BaseConsumpManager baseConsumpManager = new BaseConsumpManager(); + CalorieManager calorieManager = new CalorieManager(); + compositeCountryManager = new CompositeCountryManager(baseConsumpManager); + lpjYieldReader = new LPJYieldResponseMapReader(desiredProjection); + animalRateManager = new AnimalRateManager(compositeCountryManager); + + if (ModelConfig.DEMAND_FROM_FILE) + demandManager = new DemandManagerFromFile(compositeCountryManager,calorieManager); + else if (ModelConfig.PRICE_ELASTIC_DEMAND) + demandManager = new ElasticDemandManager(ModelConfig.SSP_SCENARIO, baseConsumpManager,calorieManager, compositeCountryManager); + else + demandManager = new DemandManagerSSP(ModelConfig.SSP_SCENARIO, baseConsumpManager,calorieManager, compositeCountryManager); + + currentIrrigationData = getFixedIrrigationData(); + countryBoundaryRaster = getCountryBoundaryRaster(); + clusterIdRaster = ModelConfig.GENERATE_NEW_YIELD_CLUSTERS ? new RasterSet<IntegerRasterItem>(desiredProjection) : getClusterRaster(); + + globalLandUseRaster = new RasterSet<LandUseItem>(desiredProjection); + internationalMarket = new InternationalMarket(); + + woodYieldReader = new WoodYieldReader(desiredProjection); + carbonFluxReader = new CarbonFluxReader(desiredProjection); + + createCountryAgents(compositeCountryManager.getAll()); + } + + /* run the model */ + private void run() { + for (int i = ModelConfig.START_TIMESTEP; i <= ModelConfig.END_TIMESTEP; i++) { + Timestep timestep = new Timestep(i); + try { + doTimestep(timestep); + } catch (Exception e) { + LpjgOutputer.writeMarkerFile(timestep.getYear(), true); + throw new RuntimeException(e); + } + } + } + + private void doTimestep(Timestep timestep) { + System.gc(); + LogWriter.println("Timestep: " + timestep.toString()); + LogWriter.println("Memory usage 1: " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())); + + YieldRaster yieldSurfaces = getYieldSurfaces(timestep); // this will wait for the marker file from LPJ if configured to do so + getUpdateIrrigationData(timestep, yieldSurfaces); // updating currentIrrigationData + + // When running half earth we can to alter protected areas data at a point in time + if(ModelConfig.HALFEARTH && ModelConfig.FORCE_PROTECTED_AREAS_START_YEAR == timestep.getYear() && !ModelConfig.IS_CALIBRATION_RUN) { + new ProtectedAreasReader(globalLandUseRaster).getRasterDataFromFile(ModelConfig.HALF_EARTH_FILE); + countryAgents.updateProtectedAreasForAll(globalLandUseRaster); + } + + double carbonDemandIncrease; + double carbonDemand; + if (ModelConfig.IS_CARBON_ON) { + double previousCarbonDemand = (timestep.isInitialTimestep() || ModelConfig.IS_CALIBRATION_RUN ) ? 0: demandManager.getCarbonDemand(timestep.getPreviousTimestep()); + carbonDemand = demandManager.getCarbonDemand(ModelConfig.IS_CALIBRATION_RUN ? new Timestep(0) : timestep); + + carbonDemandIncrease = (carbonDemand > previousCarbonDemand) ? carbonDemand - previousCarbonDemand: 0; + } else { + carbonDemandIncrease = 0; + carbonDemand = 0; + } + + WoodYieldRasterSet woodYieldData = getWoodYieldData(timestep); + CarbonFluxRasterSet carbonFluxData = getCarbonFluxData(timestep); + + Map<LccKey, Double> conversionCosts; + conversionCosts = (ModelConfig.CONVERSION_COSTS_FROM_FILE) ? new ConversionCostReader().read() : new ConversionCostReader().calcFromConfig(); + + countryAgents.determineProductionForAll(timestep, yieldSurfaces, currentIrrigationData, carbonFluxData, woodYieldData, + conversionCosts, carbonDemandIncrease); + internationalMarket.determineInternationalTrade(countryAgents.getAll(), carbonDemand, timestep); + + int i = 0; + while (i < ModelConfig.DEMAND_RECALC_MAX_ITERATIONS || + (ModelConfig.DEMAND_RECALC_ON_NEGATIVE_STOCK && internationalMarket.negativeStockLevelsExist() && i<10)) { // loop if negative stock have we haven't tried 10 times already + LogWriter.println("\n++ Re-estimating prices and demand: timestep " + timestep.getTimestep() + ": interation " + i); + countryAgents.recalculateDemandAndNetImportsForAll(); // recalculate demand from new prices and calculate imports and exports + internationalMarket.determineInternationalTrade(countryAgents.getAll(), carbonDemand, timestep); // calculate prices + i++; + } + internationalMarket.applyPriceShocks(timestep); + + LogWriter.println("Memory usage: 2" + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())); + + // output results + outputTimestepResults(timestep); + checkAndSaveCheckpoint(timestep); + + } + + private void checkAndSaveCheckpoint(Timestep timestep) { + if (ModelConfig.IS_CALIBRATION_RUN || shouldSaveCheckpoint(timestep)) + serializeCheckpoint(); + } + + private boolean shouldSaveCheckpoint(Timestep timestep) { + if (ModelConfig.CHECKPOINT_YEARS != null) { + LogWriter.println("Looking to see if checkpoint year reached " + ModelConfig.CHECKPOINT_YEARS); + + String[] yearStr = ModelConfig.CHECKPOINT_YEARS.split(","); + for(int i=0; i<yearStr.length; i++) { + LogWriter.println("Got a checkpoint yearStr " + yearStr[i]); + int year = Integer.parseInt(yearStr[i]); + if (timestep.getYear() == year) + return true; + } + } + return false; + } + + private void writeLandCoverFile(Timestep timestep, RasterSet<LandUseItem> landUseRaster) { + try { + StringBuffer sbHeadings = new StringBuffer("Year,Cropland,Pasture,TimberForest,CarbonForest,Natural,AbPasture,Suitable,EnergyCrop,FertCrop,IrrigCrop"); + BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.LAND_COVER_OUTPUT_FILE, sbHeadings.toString()); + + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f", timestep.getYear(), + LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.CROPLAND), + LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.PASTURE), + LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.TIMBER_FOREST), + LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.CARBON_FOREST), + LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.NATURAL), + LandUseItem.getAbandonedPasture(landUseRaster.values()), + LandUseItem.getSuitableTotal(landUseRaster.values(), timestep.getYear())) + ); + + sbData.append(String.format(",%.1f", LandUseItem.getTotalCropArea(landUseRaster.values(), CropType.ENERGY_CROPS))); + sbData.append(String.format(",%.1f", LandUseItem.getFertiliserTotal(landUseRaster.values(), CropType.getCropsLessPasture()) / 1000)); + sbData.append(String.format(",%.1f", LandUseItem.getIrrigationTotal(landUseRaster.values(), CropType.getCropsLessPasture()))); + + outputFile.write(sbData.toString()); + outputFile.newLine(); + outputFile.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void writeGlobalMarketFile(Timestep timestep) { + try { + BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.PRICES_OUTPUT_FILE, "Year,Crop,Imports (Mt),Exports (Mt),New export price, Stock Levels (Mt)"); + internationalMarket.writeGlobalMarketFile(timestep, outputFile); + outputFile.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void writeGlobalFoodBalanceSheet(Timestep timestep, RasterSet<LandUseItem> landUseRaster) { + try { + BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.FOOD_BALANCE_SHEET_FILE, "Year,Crop,Production,Imports,Export,TransportLosses,StockVar,Supply,MonogastricsFeed,RuminantsFeed,SeedAndOtherLosses,FoodAnd1stGen,ProdArea"); + Map<CropType, GlobalPrice> worldPrices = internationalMarket.getWorldPrices(); + double harvestedAndFallowArea = 0; + + for (CropType crop : CropType.getCropsLessPasture()) { + GlobalPrice priceQuantity = worldPrices.get(crop); // some specific logic for import/exports and this has been aggregated already, so best to use it + double prod=0, prodArea=0, feedMonogastrics=0, feedRuminants=0; + double exportsBeforeTL=0, imports=0, transportloss=0, stockChange=0; + + if (priceQuantity != null) { + exportsBeforeTL = priceQuantity.getExportsBeforeTransportLoss(); + imports = priceQuantity.getImportAmount(); + transportloss = priceQuantity.getTransportLosses(); + stockChange = priceQuantity.getStockChange(); + } + + for (AbstractCountryAgent ca : countryAgents.getAll()) { + Map<CropType, CropUsageData> allCropUsage = ca.getCropUsageData(); + CropUsageData cropUsage = allCropUsage.get(crop); + if (cropUsage != null) { + prod += cropUsage.getProductionExpected(); + prodArea += cropUsage.getArea(); + feedMonogastrics += cropUsage.getMonogastricFeed(); + feedRuminants += cropUsage.getRuminantFeed(); + } + } + + double seedAndWaste = prod * crop.getSeedAndWasteRate(); + double netSupply = prod - exportsBeforeTL + imports; + double foodAnd1stGen = netSupply - feedMonogastrics - feedRuminants - seedAndWaste; + + if (!crop.equals(CropType.SETASIDE)) + prodArea *= (1-ModelConfig.UNHANDLED_CROP_RATE); // remove unhandled crop area + + harvestedAndFallowArea += prodArea; + + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%s", timestep.getYear(), crop.getGamsName())); + sbData.append(String.format(",%.2f", prod)); + sbData.append(String.format(",%.2f,%.2f,%.2f,%.2f", imports, exportsBeforeTL, transportloss, stockChange)); + sbData.append(String.format(",%.2f,%.2f,%.2f,%.2f,%.2f", netSupply, feedMonogastrics, feedRuminants, seedAndWaste, foodAnd1stGen)); + sbData.append(String.format(",%.2f", prodArea)); + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + + double cropLandArea = LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.CROPLAND); + double unhandledArea = cropLandArea - harvestedAndFallowArea; + outputFile.write(String.format("%d,%s,,,,,,,,,,,%.2f", timestep.getYear(), "unhandled", unhandledArea)); + outputFile.newLine(); + + outputFile.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void writeDemandFile(Timestep timestep) { + try { + BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.DEMAND_OUTPUT_FILE, "Year,Commodity,Amount (Mt)"); + + for (CommodityType comm : CommodityType.getAllFoodItems()) { + double demandAmount = 0; + + for (AbstractCountryAgent country : countryAgents.getAll()) { + Map<CommodityType, Double> demands = country.getCurrentProjectedDemand(); + if (demands == null) { + LogWriter.printlnError(country.getCountry() + " " + comm); + } + + Double d = demands.get(comm); + if (d != null) { + demandAmount += d.doubleValue(); + LogWriter.println(String.format("%s,%s,%.4f", country.getCountry(), comm.getGamsName(), d)); + } + } + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%s", timestep.getYear(), comm.getGamsName())); + sbData.append(String.format(",%.1f", demandAmount)); + + LogWriter.println("Global demand " + timestep.getYear() + " " + comm.getGamsName() + " " + demandAmount + "\n"); + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + + double gen2EcDemand = countryAgents.getAll().stream().mapToDouble(c -> c.getCurrentGen2EcDemand()).sum(); + outputFile.write(String.format("%d,%s,%.1f", timestep.getYear(), CropType.ENERGY_CROPS.getGamsName(), gen2EcDemand)); + outputFile.newLine(); + + outputFile.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void writeDomesticProductionFile(Timestep timestep) { + try { + StringBuffer sbHeadings = new StringBuffer("Year, Country, Crop, Area, Production, Production_cost, Import_price, Export_price, Consumer_price, Net_imports, Net_import_cost, Prod_shock, Rum_feed_amount, Mon_feed_amount"); + BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.DOMESTIC_OUTPUT_FILE, sbHeadings.toString()); + + for (CropType crop : CropType.getAllItems()) { + for (AbstractCountryAgent country : countryAgents.getAll()) { + + Map<CropType, CropUsageData> cropUsageAllCrops = country.getCropUsageData(); + CropUsageData cropUsage = cropUsageAllCrops.get(crop); + + if (cropUsage == null) + continue; + + double prodCosts = cropUsage.getTotalProdCost(); + double prod = cropUsage.getProductionExpected(); + double prodShock = cropUsage.getProductionShock(); + double area = cropUsage.getArea(); + double rumFeedAmount = cropUsage.getRuminantFeed(); + double monFeedAmount = cropUsage.getMonogastricFeed(); + + double importPrice = 0; + double exportPrice = 0 ; + double consumerPrice = 0; + double netImports = 0; + double netImportCost = 0; + + if (crop.isImportedCrop()) { + CountryPrice px = country.getCurrentCountryPrices().get(crop); + importPrice = px.getImportPrice(); + exportPrice = px.getExportPrice(); + consumerPrice = px.getConsumerPrice(); + netImports = cropUsage.getNetImportsExpected(); //this isn't accounting for transport losses in exports + netImportCost = cropUsage.getNetImportCostExpected(); + } + + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%s,%s", timestep.getYear(), country.getCountry(), crop.getGamsName())); + sbData.append(String.format(",%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f", area, prod, prodCosts, importPrice, exportPrice, consumerPrice, netImports, netImportCost, prodShock, rumFeedAmount, monFeedAmount)); + + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + } + outputFile.close(); + + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void writeWoodAndCarbonProdFile(Timestep timestep) { + try { + 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()) { + + Map<WoodType, WoodUsageData> woodUsageMap = country.getWoodUsageData(); + + for (WoodType woodType : WoodType.values()) { + WoodUsageData woodUsage = woodUsageMap.get(woodType); + + if (woodUsage == null) + continue; + + double prod = woodUsage.getHarvest(); + double netImports = woodUsage.getNetImport(); + + CountryPrice px = country.getCurrentCountryWoodPrice(); + double importPrice = px.getImportPrice(); + double exportPrice = px.getExportPrice(); + + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%s,%s", timestep.getYear(), country.getCountry(), woodType.getName())); + sbData.append(String.format(",%.4f,%.4f,%.4f,%.4f", prod, importPrice, exportPrice, netImports)); + + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + } + outputFile.close(); + + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void writeCountryDemandFile(Timestep timestep){ + + try { + StringBuffer sbHeadings = new StringBuffer("Year, Country, Commodity, Demand, BioenergyDemand"); + BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.COUNTRY_DEMAND_FILE, sbHeadings.toString()); + + for (AbstractCountryAgent country : countryAgents.getAll()) { + for (CommodityType commodity : CommodityType.getAllFoodItems()) { + double bioenergyDemand = demandManager.getFirstGenBioenergyDemand(country.getCountry(), timestep.getYear(), commodity); + double demand = country.getCurrentProjectedDemand().get(commodity); + + StringBuffer sbData = new StringBuffer(); + sbData.append(String.format("%d,%s,%s", timestep.getYear(), country.getCountry(), commodity.getGamsName())); + sbData.append(String.format(",%.3f,%.3f", demand, bioenergyDemand)); + + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + } + outputFile.close(); + + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void writeAnimalNumber(Timestep timestep) { + try { + StringBuffer sbHeadings = new StringBuffer("Year,Country,FAOItem,Heads(M)"); + BufferedWriter outputFile = FileWriterHelper.getFileWriter(timestep, ModelConfig.ANIMAL_NUMBERS_OUTPUT_FILE, sbHeadings.toString()); + + for (AbstractCountryAgent country : countryAgents.getAll()) { + Map<CropType, CropUsageData> cropUsageAllCrops = country.getCropUsageData(); + for (CropType crop : CropType.getMeatTypes()) { + CropUsageData cropusage = cropUsageAllCrops.get(crop); + if (cropusage == null) + continue; + double prod = cropusage.getProductionExpected(); + + Map<String, Double> animalRates = animalRateManager.getAnimalRates(country.getCountry(), crop); + for (Entry<String, Double> entry : animalRates.entrySet()) { + StringBuffer sbData = new StringBuffer(); + double animalNum = prod * entry.getValue(); + sbData.append(String.format("%d,%s,%s,%.4f", timestep.getYear(), country.getCountry(), entry.getKey(), animalNum)); + outputFile.write(sbData.toString()); + outputFile.newLine(); + } + } + } + outputFile.close(); + + } catch (IOException e) { + LogWriter.print(e); + } + } + + private void outputTimestepResults(Timestep timestep) { + + writeLandCoverFile(timestep, globalLandUseRaster); + writeGlobalMarketFile(timestep); + writeDemandFile(timestep); + writeDomesticProductionFile(timestep); + writeWoodAndCarbonProdFile(timestep); + writeCountryDemandFile(timestep); + writeGlobalFoodBalanceSheet(timestep, globalLandUseRaster); + writeAnimalNumber(timestep); + + if (ModelConfig.OUTPUT_FOR_LPJG) { + for (int outputYear : timestep.getYearsFromLast()) { + LogWriter.println("Outputing Year: " + outputYear); + RasterSet<LandUseItem> landUseToOutput = null; + + if (outputYear == timestep.getYear()) { + landUseToOutput = globalLandUseRaster; + } + else if (ModelConfig.INTERPOLATE_OUTPUT_YEARS) { + // we run with 1 year time steps these days so this code is redundant and flawed + /* InterpolatingRasterSet<LandUseItem> intermediateLandUse = new InterpolatingRasterSet<LandUseItem>( landUseRaster.getHeaderDetails()) { + private static final long serialVersionUID = 1306045141011047760L; + protected LandUseItem createRasterData() { + return new LandUseItem(); + } + }; + intermediateLandUse.setup(globalLandUseRaster, landUseRaster, timestep.getPreviousTimestep().getYear(), timestep.getYear(), outputYear); + landUseToOutput = intermediateLandUse; */ + } + + if (landUseToOutput != null) { + LpjgOutputer lpjOutputer = new LpjgOutputer(outputYear, landUseToOutput); + lpjOutputer.writeOutput(); + } + } + outputWaterAvailablity(timestep, currentIrrigationData); // uses the year directory structure created above + } + + if (timestep.isInitialTimestep() && ModelConfig.GENERATE_NEW_YIELD_CLUSTERS) + outputClusters(clusterIdRaster); + + // Output LandUses to tabular file, for analysis (perhaps) + LogWriter.println("Outputing land uses Year: " + timestep.getYear()); + LandUseOutputer landuseOutputer = new LandUseOutputer(timestep.getYear(), globalLandUseRaster); + landuseOutputer.writeOutput(); + + // don't really need this a LPJ outputs have same data, although in a slightly different format + // outputLandCover(timestep.getYear(), landUseRaster, LandCoverType.CROPLAND); + // outputLandCover(timestep.getYear(), landUseRaster, LandCoverType.PASTURE); + } + + + private void outputWaterAvailablity(Timestep timestep, IrrigationRasterSet irrigiationRS) { + new RasterOutputer<Double, IrrigationItem>(irrigiationRS, ModelConfig.OUTPUT_DIR + File.separator + timestep.getYear() + File.separator + "IrrigConstraint.asc") { + @Override + public Double getValue(RasterKey location) { + IrrigationItem item = results.get(location); + if (item == null) + return null; + + return item.getIrrigConstraint(); + } + }.writeOutput(); + } + + private void outputClusters(RasterSet<IntegerRasterItem> landUseRaster) { + new RasterOutputer<Integer, IntegerRasterItem>(landUseRaster, ModelConfig.CLUSTERED_YIELD_FILE) { + @Override + public Integer getValue(RasterKey location) { + IntegerRasterItem item = results.get(location); + if (item == null) + return null; + + return item.getInt(); + } + }.writeOutput(); + } + + public RasterSet<IntegerRasterItem> getClusterRaster() { + RasterSet<IntegerRasterItem> clusters = new RasterSet<IntegerRasterItem>(desiredProjection) { + private static final long serialVersionUID = 2467452274591854417L; + + @Override + protected IntegerRasterItem createRasterData() { + return new IntegerRasterItem(0); + } + }; + + IntegerRasterReader clusterReader = new IntegerRasterReader(clusters); + clusterReader.getRasterDataFromFile(ModelConfig.CLUSTERED_YIELD_FILE); + return clusters; + } + + public CountryBoundaryRaster getCountryBoundaryRaster() { + CountryBoundaryRaster countryBoundaries = new CountryBoundaryRaster(desiredProjection, compositeCountryManager); + CountryBoundaryReader countryReader = new CountryBoundaryReader(countryBoundaries); + countryReader.getRasterDataFromFile(ModelConfig.COUNTRY_BOUNDARY_FILE); + return countryBoundaries; + } + + public void createCountryAgents(Collection<CompositeCountry> countryGrouping) { + countryAgents = new CountryAgentManager(compositeCountryManager, demandManager, countryBoundaryRaster, internationalMarket, clusterIdRaster, globalLandUseRaster); + Map<CompositeCountry, Map<CropType, CropUsageData>> cropUsageDataMap = getInitialCropUsageData(); + Map<CompositeCountry, Map<WoodType, WoodUsageData>> woodUsageDataMap = getInitialWoodUsageData(); + RasterSet<LandUseItem> initLU = getInitialLandUse(); + + for (CompositeCountry cc : countryGrouping) { + countryAgents.addForCountry(cc, cropUsageDataMap, initLU, woodUsageDataMap); + globalLandUseRaster.putAll(initLU); + } + } + + private RasterSet<LandUseItem> getInitialLandUse() { + RasterSet<LandUseItem> initialLU; + if (ModelConfig.IS_CALIBRATION_RUN) + initialLU = getLandUseFromBaseline(); + else + initialLU = deserializeLandUse(); + + return initialLU; + } + + private Map<CompositeCountry, Map<CropType, CropUsageData>> getInitialCropUsageData() { + Map<CompositeCountry, Map<CropType, CropUsageData>> cropUsageDataMap; + if (ModelConfig.IS_CALIBRATION_RUN) + cropUsageDataMap = new CropUsageReader(compositeCountryManager).getCommodityData(); + else + cropUsageDataMap = deserializeCropUsage(); + + return cropUsageDataMap; + } + + private Map<CompositeCountry, Map<WoodType, WoodUsageData>> getInitialWoodUsageData() { + Map<CompositeCountry, Map<WoodType, WoodUsageData>> woodUsageDataMap; + if (ModelConfig.IS_CALIBRATION_RUN) { + woodUsageDataMap = new WoodUsageReader(compositeCountryManager).getWoodUsageData(); + + } else { + woodUsageDataMap = deserializeWoodUsage(); + } + return woodUsageDataMap; + } + + @SuppressWarnings("unchecked") + private Map<CompositeCountry, Map<WoodType, WoodUsageData>> deserializeWoodUsage() { + try { + Map<CompositeCountry, Map<WoodType, WoodUsageData>> woodUsageDataMap; + FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_WOOD_USAGE_FILE); + ObjectInputStream in = new ObjectInputStream(fileIn); + woodUsageDataMap = (Map<CompositeCountry, Map<WoodType, WoodUsageData>>) in.readObject(); + in.close(); + fileIn.close(); + LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_WOOD_USAGE_FILE); + return woodUsageDataMap; + } catch (IOException i) { + LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_WOOD_USAGE_FILE); + LogWriter.print(i); + return null; + } catch (ClassNotFoundException c) { + LogWriter.printlnError("Map<CompositeCountry, Double[]> not found"); + c.printStackTrace(); + return null; + } + } + + private void serializeLandUse(RasterSet<LandUseItem> landUseRaster) { + RasterSet<LandUseItem> rasterToSerialise = new RasterSet<LandUseItem>(desiredProjection); + for (Map.Entry<RasterKey, LandUseItem> entry : landUseRaster.entrySet()) { + LandUseItem newLuItem = new LandUseItem(entry.getValue()); + newLuItem.overwriteLandCoverAreas(new HashMap<LandCoverType, LandCoverTile>()); // Clear data + rasterToSerialise.put(entry.getKey(), newLuItem); + } + try { + LogWriter.println("Starting serializing LandUse to " + ModelConfig.SERIALIZED_LAND_USE_FILE); + FileOutputStream fileOut = new FileOutputStream(ModelConfig.SERIALIZED_LAND_USE_FILE); + ObjectOutputStream out = new ObjectOutputStream(fileOut); + out.writeObject(rasterToSerialise); + out.close(); + fileOut.close(); + LogWriter.println("Serialized data is saved"); + } catch (IOException i) { + i.printStackTrace(); + } + + LogWriter.println("Starting serializing LandCover to " + ModelConfig.SERIALIZED_LAND_COVER_FILE); + LandUseBinarySerializer luSer = new LandUseBinarySerializer(); + luSer.serializeLandUse(globalLandUseRaster); + LogWriter.println("LandCover data is saved"); + } + + @SuppressWarnings("unchecked") + private RasterSet<LandUseItem> deserializeLandUse() { + try { + RasterSet<LandUseItem> initLU; + FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_LAND_USE_FILE); + ObjectInputStream in = new ObjectInputStream(fileIn); + initLU = (RasterSet<LandUseItem>) in.readObject(); + in.close(); + fileIn.close(); + LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_LAND_USE_FILE); + LandUseBinarySerializer luSer = new LandUseBinarySerializer(); + luSer.deserializeLandUse(initLU); + return initLU; + } catch (IOException i) { + LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_LAND_USE_FILE); + LogWriter.print(i); + return null; + } catch (ClassNotFoundException c) { + LogWriter.printlnError("RasterSet<LandUseItem> class not found"); + c.printStackTrace(); + return null; + } + } + + @SuppressWarnings("unchecked") + private Map<CompositeCountry, Map<CropType, CropUsageData>> deserializeCropUsage() { + try { + Map<CompositeCountry, Map<CropType, CropUsageData>> initCropUsage; + FileInputStream fileIn = new FileInputStream(ModelConfig.SERIALIZED_CROP_USAGE_FILE); + ObjectInputStream in = new ObjectInputStream(fileIn); + initCropUsage = (Map<CompositeCountry, Map<CropType, CropUsageData>>) in.readObject(); + in.close(); + fileIn.close(); + LogWriter.println("Deserialized " + ModelConfig.SERIALIZED_CROP_USAGE_FILE); + return initCropUsage; + } catch (IOException i) { + LogWriter.printlnError("Problem deserializing " + ModelConfig.SERIALIZED_CROP_USAGE_FILE); + LogWriter.print(i); + return null; + } catch (ClassNotFoundException c) { + LogWriter.printlnError("Map<CompositeCountry, Map<CropType, CropUsageData>> not found"); + c.printStackTrace(); + return null; + } + } + + + /** this is if we are starting from Hurtt of other initial land covers (so we don't have land uses and intensity data) */ + private RasterSet<LandUseItem> getLandUseFromBaseline() { + RasterSet<LandCoverItem> initialLC = new RasterSet<LandCoverItem>(desiredProjection) { + private static final long serialVersionUID = 4642550777741425501L; + + protected LandCoverItem createRasterData() { + return new LandCoverItem(); + } + }; + + new MaxCropAreaReader(initialLC).getRasterDataFromFile(ModelConfig.HIGH_SLOPE_AREAS_FILE); // Fraction unavailable for conversion + new LandCoverReader(initialLC).getRasterDataFromFile(ModelConfig.INITAL_LAND_COVER_FILE); // Land cover fractions + new LandTileReader(initialLC).getRasterDataFromFile(ModelConfig.LAND_COVER_AGE_DIST_FILENAME); // Age distribution of land cover + new InitProtectedAreasReader(initialLC).getRasterDataFromFile(ModelConfig.PROTECTED_AREAS_FILE); // Protected fraction + + RasterSet<LandUseItem> landUseRaster = new RasterSet<LandUseItem>(initialLC.getHeaderDetails()); + + for (Map.Entry<RasterKey, LandCoverItem> entry : initialLC.entrySet()) { + //LogWriter.println(initialLC.getXCoordin(entry.getKey()) + " " + initialLC.getYCoordin(entry.getKey())); + landUseRaster.put(entry.getKey(), new LandUseItem(entry.getValue())); + } + + return landUseRaster; + } + + private YieldRaster getYieldSurfaces(Timestep timestep) { + return lpjYieldReader.getRasterData(timestep); + } + + /** Get irrigation data that does not change with time, should only be called once */ + private IrrigationRasterSet getFixedIrrigationData() { + IrrigationRasterSet fixedIrrigData = new IrrigationRasterSet(desiredProjection, new FPUManager(desiredProjection)); + new IrrigiationCostReader(fixedIrrigData).getRasterDataFromFile(ModelConfig.IRRIGATION_COST_FILE); + new IrrigationConstraintReader(fixedIrrigData).getRasterDataFromFile(ModelConfig.IRRIGATION_CONSTRAINT_FILE); + + String baseTimestepRootDir = Timestep.getYearSubDir(ModelConfig.YIELD_DIR, ModelConfig.ELLIOTT_BASEYEAR); // needs to be Elliott base timestep + new RunOffReader(fixedIrrigData, true).getRasterDataFromFile(baseTimestepRootDir + File.separator + ModelConfig.IRRIG_RUNOFF_FILE); + + fixedIrrigData.calcIrrigConstraintOffsets(); // should have everything we need to calc offset between Elliott and LPJ data + return fixedIrrigData; + } + + /** Get carbon flux data */ + private CarbonFluxRasterSet getCarbonFluxData(Timestep timestep) { + if (ModelConfig.IS_CARBON_ON) { + return carbonFluxReader.getCarbonFluxes(globalLandUseRaster, timestep); + } else { + return new CarbonFluxRasterSet(desiredProjection); + } + } + + /** Get wood yield data */ + private WoodYieldRasterSet getWoodYieldData(Timestep timestep) { + if (ModelConfig.IS_FORESTRY_ON) { + return woodYieldReader.getWoodYields(globalLandUseRaster, timestep, internationalMarket.getWoodPrice().getExportPrice()); + } else { + return new WoodYieldRasterSet(desiredProjection); + } + } + + /** Ugly in situ update of currentIrrigationData, better if IrrigationRasterSets were handled more immutably */ + private void getUpdateIrrigationData(Timestep timestep, YieldRaster yieldSurfaces) { + String rootDir = timestep.getYearSubDir(ModelConfig.YIELD_DIR); + + new IrrigationMaxAmountReader(currentIrrigationData).getRasterDataFromFile(rootDir + File.separator + ModelConfig.IRRIG_MAX_WATER_FILENAME); + + if (!ModelConfig.USE_BLUE_WATER_FILE_IRRIG_CONSTRAINT) { + new RunOffReader(currentIrrigationData, false).getRasterDataFromFile(rootDir + File.separator + ModelConfig.IRRIG_RUNOFF_FILE); + currentIrrigationData.updateIrrigConstraints(timestep); + } + } + + private void serializeCheckpoint() { + serializeLandUse(globalLandUseRaster); + countryAgents.serializeCropUsageForAll(); + countryAgents.serializeWoodUsageForAll(); + internationalMarket.serializeGlobalPrices(); + } +} diff --git a/src/ac/ed/lurg/carbon/CarbonFluxItem.java b/src/ac/ed/lurg/carbon/CarbonFluxItem.java index 25d9abbaf580427775eeea6b35b7534d47d0ab45..ebd8c875c851ae82b79a5ca315a6926fcc7eb7b1 100644 --- a/src/ac/ed/lurg/carbon/CarbonFluxItem.java +++ b/src/ac/ed/lurg/carbon/CarbonFluxItem.java @@ -1,81 +1,107 @@ -package ac.ed.lurg.carbon; - -import ac.sac.raster.RasterItem; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.Timestep; -import ac.ed.lurg.landuse.LandCoverTile; -import ac.ed.lurg.landuse.LccKey; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.LandProtectionType; - -public class CarbonFluxItem implements RasterItem, Serializable { - private static final long serialVersionUID = 440720456140537815L; - - Map<LccKey, Double> conversionCarbonFlux = new HashMap<LccKey, Double>(); - Map<LandCoverType, Double> ecosystemCarbonFlux = new HashMap<LandCoverType, Double>(); - - public void setConversionCarbonFlux(LccKey key, double cFlux) { - conversionCarbonFlux.put(key, cFlux); - } - - public void calcConversionCarbonFlux(LccKey key, Double[] cFluxes, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep) { - LandCoverTile tiles = landUseTiles.get(key.getFromLc()); - - double totalArea = tiles.getTotalArea(LandProtectionType.CONVERTIBLE); - if (totalArea == 0) { - this.conversionCarbonFlux.put(key, 0.0); - } else { - double totalFlux = 0; - for (int age=0; age<=LandCoverTile.getMaxAgeBin(); age++) { - int ageCapped = Math.min(age, ModelConfig.CARBON_WOOD_MAX_TIME - 1); - totalFlux += cFluxes[ageCapped] * tiles.getArea(LandProtectionType.CONVERTIBLE, age); - } - double meanFlux = totalFlux / totalArea; - this.conversionCarbonFlux.put(key, meanFlux); - } - } - - public void setEcosystemCarbonFlux(LandCoverType lcType, double cFlux) { - ecosystemCarbonFlux.put(lcType, cFlux); - } - - public void calcEcosystemCarbonFlux(LandCoverType lcType, Double[] cFluxes, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep) { - - LandCoverTile tiles = landUseTiles.get(lcType); - - double totalArea = tiles.getTotalArea(LandProtectionType.CONVERTIBLE); - if (totalArea == 0) { - this.ecosystemCarbonFlux.put(lcType, 0.0); - } else { - double totalFlux = 0; - for (int age=0; age<=LandCoverTile.getMaxAgeBin(); age++) { - int ageCapped = Math.min(age, ModelConfig.CARBON_WOOD_MAX_TIME - 1); - totalFlux += cFluxes[ageCapped] * tiles.getArea(LandProtectionType.CONVERTIBLE, age); - } - double meanFlux = totalFlux / totalArea; - this.ecosystemCarbonFlux.put(lcType, meanFlux); - } - } - - public double getConversionCarbonFlux(LccKey key) { - return conversionCarbonFlux.getOrDefault(key, Double.NaN); - } - - public double getEcosystemCarbonFlux(LandCoverType lcType) { - return ecosystemCarbonFlux.getOrDefault(lcType, Double.NaN); - } - - public Map<LccKey, Double> getConversionCarbonFluxMap() { - return conversionCarbonFlux; - } - - public Map<LandCoverType, Double> getEcosystemCarbonFluxMap() { - return ecosystemCarbonFlux; - } - -} +package ac.ed.lurg.carbon; + +import ac.sac.raster.RasterItem; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.Timestep; +import ac.ed.lurg.landuse.LandCoverTile; +import ac.ed.lurg.landuse.LccKey; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; + +public class CarbonFluxItem implements RasterItem, Serializable { + private static final long serialVersionUID = 440720456140537815L; + + Map<LccKey, Double> conversionCarbonFlux = new HashMap<LccKey, Double>(); + Map<LandCoverType, Double> ecosystemCarbonFlux = new HashMap<LandCoverType, Double>(); + Map<LandCoverType, Double> carbonHorizonFlux = new HashMap<LandCoverType, Double>(); + + public void setConversionCarbonFlux(LccKey key, double cFlux) { + conversionCarbonFlux.put(key, cFlux); + } + + public void calcConversionCarbonFlux(LccKey key, Double[] cFluxes, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep) { + LandCoverTile tiles = landUseTiles.get(key.getFromLc()); + + double totalArea = tiles.getTotalArea(LandProtectionType.CONVERTIBLE); + if (totalArea == 0) { + this.conversionCarbonFlux.put(key, 0.0); + } else { + double totalFlux = 0; + for (int age : tiles.getAgeKeys()) { + int ageCapped = Math.min(age, ModelConfig.CARBON_WOOD_MAX_TIME - 1); + totalFlux += cFluxes[ageCapped] * tiles.getArea(LandProtectionType.CONVERTIBLE, age); + } + double meanFlux = totalFlux / totalArea; + this.conversionCarbonFlux.put(key, meanFlux); + } + } + + public void setEcosystemCarbonFlux(LandCoverType lcType, double cFlux) { + ecosystemCarbonFlux.put(lcType, cFlux); + } + + public void calcEcosystemCarbonFlux(LandCoverType lcType, Double[] cFluxes, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep) { + + LandCoverTile tiles = landUseTiles.get(lcType); + + double totalArea = tiles.getTotalArea(LandProtectionType.CONVERTIBLE); + if (totalArea == 0) { + this.ecosystemCarbonFlux.put(lcType, 0.0); + } else { + double totalFlux = 0; + for (int age : tiles.getAgeKeys()) { + int ageCapped = Math.min(age, ModelConfig.CARBON_WOOD_MAX_TIME - 1); + totalFlux += cFluxes[ageCapped] * tiles.getArea(LandProtectionType.CONVERTIBLE, age); + } + double meanFlux = totalFlux / totalArea; + this.ecosystemCarbonFlux.put(lcType, meanFlux); + } + } + + public void calcCarbonHorizonFlux(LandCoverType lcType, Double[] cFluxes) { + double totalFlux = 0; + for (int i = 0; i < ModelConfig.CARBON_HORIZON; i++) { + totalFlux += cFluxes[i]; + } + carbonHorizonFlux.put(lcType, totalFlux); + } + + + + public void setCarbonHorizonFlux(LandCoverType lcType, Double cFlux) { + carbonHorizonFlux.put(lcType, cFlux); + } + + public double getConversionCarbonFlux(LccKey key) { + return conversionCarbonFlux.getOrDefault(key, Double.NaN); + } + + public double getEcosystemCarbonFlux(LandCoverType lcType) { + return ecosystemCarbonFlux.getOrDefault(lcType, Double.NaN); + } + + public double getCarbonHorizonFlux(LandCoverType lcType) { + return carbonHorizonFlux.getOrDefault(lcType, Double.NaN); + } + + public Map<LandCoverType, Double> getCarbonHorizonFlux() { + return carbonHorizonFlux; + } + + public Map<LccKey, Double> getConversionCarbonFluxMap() { + return conversionCarbonFlux; + } + + public Map<LandCoverType, Double> getEcosystemCarbonFluxMap() { + return ecosystemCarbonFlux; + } + + public Map<LandCoverType, Double> getCarbonHorizonFluxMap() { + return carbonHorizonFlux; + } +} diff --git a/src/ac/ed/lurg/carbon/CarbonFluxReader.java b/src/ac/ed/lurg/carbon/CarbonFluxReader.java index 3f5ed1c3565714cd3d14e3e4f70249b0fa63dad9..ff587e836110b8aa2fcdd9a552497f72c8adeff2 100644 --- a/src/ac/ed/lurg/carbon/CarbonFluxReader.java +++ b/src/ac/ed/lurg/carbon/CarbonFluxReader.java @@ -1,107 +1,108 @@ -package ac.ed.lurg.carbon; - -import java.io.File; -import java.util.Map; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.Timestep; -import ac.ed.lurg.landuse.LandUseItem; -import ac.ed.lurg.landuse.LccKey; -import ac.ed.lurg.types.LandCoverType; -import ac.sac.raster.AbstractBinaryRasterReader; -import ac.sac.raster.RasterHeaderDetails; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -public class CarbonFluxReader { - private static final int MIN_COLS = ModelConfig.CARBON_WOOD_MAX_TIME + 2; - private static final double CONVERSION_FACTOR = 10.0; // convert kgC/m2 to tC/ha - private RasterHeaderDetails rasterProj; - private String[] header = new String[MIN_COLS]; - - public CarbonFluxReader(RasterHeaderDetails rasterProj) { - this.rasterProj = rasterProj; - header[0] = "Lon"; - header[1] = "Lat"; - for (int i = 0; i < MIN_COLS - 1; i++) { - header[i] = Integer.toString(i); - } - } - - public CarbonFluxRasterSet getCarbonFluxes(RasterSet<LandUseItem> landUseRaster, Timestep timestep) { - CarbonFluxRasterSet cFluxData = new CarbonFluxRasterSet(rasterProj); - - getCarbonFluxesConversion("cflux_ntrl_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.PASTURE)); - getCarbonFluxesConversion("cflux_ntrl_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.CARBON_FOREST)); - getCarbonFluxesConversion("cflux_ntrl_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.TIMBER_FOREST)); - getCarbonFluxesConversion("cflux_ntrl_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.CROPLAND)); - - getCarbonFluxesConversion("cflux_past_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.NATURAL)); - getCarbonFluxesConversion("cflux_past_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.CARBON_FOREST)); - getCarbonFluxesConversion("cflux_past_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.TIMBER_FOREST)); - getCarbonFluxesConversion("cflux_past_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.CROPLAND)); - - getCarbonFluxesConversion("cflux_forC_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.NATURAL)); - getCarbonFluxesConversion("cflux_forC_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.PASTURE)); - getCarbonFluxesConversion("cflux_forC_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.TIMBER_FOREST)); - getCarbonFluxesConversion("cflux_forC_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.CROPLAND)); - - getCarbonFluxesConversion("cflux_forC_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.NATURAL)); - getCarbonFluxesConversion("cflux_forC_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.PASTURE)); - getCarbonFluxesConversion("cflux_forC_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.CARBON_FOREST)); - getCarbonFluxesConversion("cflux_forC_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.CROPLAND)); - - getCarbonFluxesConversion("cflux_crop_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.NATURAL)); - getCarbonFluxesConversion("cflux_crop_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.PASTURE)); - getCarbonFluxesConversion("cflux_crop_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.CARBON_FOREST)); - getCarbonFluxesConversion("cflux_crop_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.TIMBER_FOREST)); - - getCarbonFluxesEcosystem("cflux_sts_ntrl.dat", cFluxData, landUseRaster, timestep, LandCoverType.NATURAL); - getCarbonFluxesEcosystem("cflux_sts_past.dat", cFluxData, landUseRaster, timestep, LandCoverType.PASTURE); - getCarbonFluxesEcosystem("cflux_sts_forC.dat", cFluxData, landUseRaster, timestep, LandCoverType.CARBON_FOREST); - getCarbonFluxesEcosystem("cflux_sts_forC.dat", cFluxData, landUseRaster, timestep, LandCoverType.TIMBER_FOREST); - getCarbonFluxesEcosystem("cflux_sts_crop.dat", cFluxData, landUseRaster, timestep, LandCoverType.CROPLAND); - - return cFluxData; - - } - - public void getCarbonFluxesConversion(String filename, CarbonFluxRasterSet cFluxData, RasterSet<LandUseItem> landUseRaster, Timestep timestep, LccKey lccKey) { - - AbstractBinaryRasterReader<CarbonFluxItem> cFluxReader = new AbstractBinaryRasterReader<CarbonFluxItem>(header, MIN_COLS - 2, cFluxData) { - protected void setData(RasterKey key, CarbonFluxItem item, Map<String, Double> rowValues) { - - Double[] fluxes = getArrayFromRowValues(rowValues); - item.calcConversionCarbonFlux(lccKey, fluxes, landUseRaster.get(key).getLandCoverTiles(), timestep); - } - }; - - cFluxReader.getRasterDataFromFile(getDataDir(filename, timestep)); - } - - public void getCarbonFluxesEcosystem(String filename, CarbonFluxRasterSet cFluxData, RasterSet<LandUseItem> landUseRaster, Timestep timestep, LandCoverType lcType) { - - AbstractBinaryRasterReader<CarbonFluxItem> cFluxReader = new AbstractBinaryRasterReader<CarbonFluxItem>(header, MIN_COLS - 2, cFluxData) { - protected void setData(RasterKey key, CarbonFluxItem item, Map<String, Double> rowValues) { - - Double[] fluxes = getArrayFromRowValues(rowValues); - item.calcEcosystemCarbonFlux(lcType, fluxes, landUseRaster.get(key).getLandCoverTiles(), timestep); - } - }; - - cFluxReader.getRasterDataFromFile(getDataDir(filename, timestep)); - } - - private Double[] getArrayFromRowValues(Map<String, Double> rowValues) { - int arrSize = rowValues.size(); - Double[] arr = new Double[arrSize]; - for (int i=0; i<arrSize; i++) { - arr[i] = rowValues.get(Integer.toString(i)) * CONVERSION_FACTOR; - } - return arr; - } - - private String getDataDir(String filename, Timestep timestep) { - return timestep.getWoodAndCarbonYearSubDir(ModelConfig.WOOD_AND_CARBON_DIR) + File.separator + filename; - } -} +package ac.ed.lurg.carbon; + +import java.io.File; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.Timestep; +import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.LccKey; +import ac.ed.lurg.types.LandCoverType; +import ac.sac.raster.AbstractBinaryRasterReader; +import ac.sac.raster.RasterHeaderDetails; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterSet; + +public class CarbonFluxReader { + private static final int MIN_COLS = ModelConfig.CARBON_WOOD_MAX_TIME + 2; + private static final double CONVERSION_FACTOR = 10.0; // convert kgC/m2 to tC/ha + private RasterHeaderDetails rasterProj; + private String[] header = new String[MIN_COLS]; + + public CarbonFluxReader(RasterHeaderDetails rasterProj) { + this.rasterProj = rasterProj; + header[0] = "Lon"; + header[1] = "Lat"; + for (int i = 0; i < MIN_COLS - 1; i++) { + header[i] = Integer.toString(i); + } + } + + public CarbonFluxRasterSet getCarbonFluxes(RasterSet<LandUseItem> landUseRaster, Timestep timestep) { + CarbonFluxRasterSet cFluxData = new CarbonFluxRasterSet(rasterProj); + + getCarbonFluxesConversion("cflux_ntrl_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.PASTURE)); + getCarbonFluxesConversion("cflux_ntrl_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.CARBON_FOREST)); + getCarbonFluxesConversion("cflux_ntrl_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.TIMBER_FOREST)); + getCarbonFluxesConversion("cflux_ntrl_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.NATURAL, LandCoverType.CROPLAND)); + + getCarbonFluxesConversion("cflux_past_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.NATURAL)); + getCarbonFluxesConversion("cflux_past_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.CARBON_FOREST)); + getCarbonFluxesConversion("cflux_past_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.TIMBER_FOREST)); + getCarbonFluxesConversion("cflux_past_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.PASTURE, LandCoverType.CROPLAND)); + + getCarbonFluxesConversion("cflux_forC_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.NATURAL)); + getCarbonFluxesConversion("cflux_forC_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.PASTURE)); + getCarbonFluxesConversion("cflux_forC_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.TIMBER_FOREST)); + getCarbonFluxesConversion("cflux_forC_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.CROPLAND)); + + getCarbonFluxesConversion("cflux_forC_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.NATURAL)); + getCarbonFluxesConversion("cflux_forC_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.PASTURE)); + getCarbonFluxesConversion("cflux_forC_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.CARBON_FOREST)); + getCarbonFluxesConversion("cflux_forC_to_crop.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.CROPLAND)); + + getCarbonFluxesConversion("cflux_crop_to_ntrl.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.NATURAL)); + getCarbonFluxesConversion("cflux_crop_to_past.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.PASTURE)); + getCarbonFluxesConversion("cflux_crop_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.CARBON_FOREST)); + getCarbonFluxesConversion("cflux_crop_to_forC.dat", cFluxData, landUseRaster, timestep, new LccKey(LandCoverType.CROPLAND, LandCoverType.TIMBER_FOREST)); + + getCarbonFluxesEcosystem("cflux_sts_ntrl.dat", cFluxData, landUseRaster, timestep, LandCoverType.NATURAL); + getCarbonFluxesEcosystem("cflux_sts_past.dat", cFluxData, landUseRaster, timestep, LandCoverType.PASTURE); + getCarbonFluxesEcosystem("cflux_sts_forC.dat", cFluxData, landUseRaster, timestep, LandCoverType.CARBON_FOREST); + getCarbonFluxesEcosystem("cflux_sts_forC.dat", cFluxData, landUseRaster, timestep, LandCoverType.TIMBER_FOREST); + getCarbonFluxesEcosystem("cflux_sts_crop.dat", cFluxData, landUseRaster, timestep, LandCoverType.CROPLAND); + + return cFluxData; + + } + + public void getCarbonFluxesConversion(String filename, CarbonFluxRasterSet cFluxData, RasterSet<LandUseItem> landUseRaster, Timestep timestep, LccKey lccKey) { + + AbstractBinaryRasterReader<CarbonFluxItem> cFluxReader = new AbstractBinaryRasterReader<CarbonFluxItem>(header, MIN_COLS - 2, cFluxData) { + protected void setData(RasterKey key, CarbonFluxItem item, Map<String, Double> rowValues) { + + Double[] fluxes = getArrayFromRowValues(rowValues); + item.calcConversionCarbonFlux(lccKey, fluxes, landUseRaster.get(key).getLandCoverTiles(), timestep); + } + }; + + cFluxReader.getRasterDataFromFile(getDataDir(filename, timestep)); + } + + public void getCarbonFluxesEcosystem(String filename, CarbonFluxRasterSet cFluxData, RasterSet<LandUseItem> landUseRaster, Timestep timestep, LandCoverType lcType) { + + AbstractBinaryRasterReader<CarbonFluxItem> cFluxReader = new AbstractBinaryRasterReader<CarbonFluxItem>(header, MIN_COLS - 2, cFluxData) { + protected void setData(RasterKey key, CarbonFluxItem item, Map<String, Double> rowValues) { + + Double[] fluxes = getArrayFromRowValues(rowValues); + item.calcEcosystemCarbonFlux(lcType, fluxes, landUseRaster.get(key).getLandCoverTiles(), timestep); + item.calcCarbonHorizonFlux(lcType, fluxes); + } + }; + + cFluxReader.getRasterDataFromFile(getDataDir(filename, timestep)); + } + + private Double[] getArrayFromRowValues(Map<String, Double> rowValues) { + int arrSize = rowValues.size(); + Double[] arr = new Double[arrSize]; + for (int i=0; i<arrSize; i++) { + arr[i] = rowValues.get(Integer.toString(i)) * CONVERSION_FACTOR; + } + return arr; + } + + private String getDataDir(String filename, Timestep timestep) { + return timestep.getWoodAndCarbonYearSubDir(ModelConfig.WOOD_AND_CARBON_DIR) + File.separator + filename; + } +} diff --git a/src/ac/ed/lurg/country/AbstractCountryAgent.java b/src/ac/ed/lurg/country/AbstractCountryAgent.java index 4701863d94358769d6068c98117e9e1a8baea926..f6cc92d43ab593df641048471cb86791e8809970 100644 --- a/src/ac/ed/lurg/country/AbstractCountryAgent.java +++ b/src/ac/ed/lurg/country/AbstractCountryAgent.java @@ -1,229 +1,237 @@ -package ac.ed.lurg.country; - -import java.util.HashMap; -import java.util.Map; - -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 { - - protected AbstractDemandManager demandManager; - protected CompositeCountry country; - protected Map<CropType, Double> tradeBarriers; - protected Map<CommodityType, Double> currentProjectedDemand; - private Map<CropType, GlobalPrice> currentWorldPrices; - protected Map<CropType, CountryPrice> currentCountryPrices; - private Map<CropType, CountryPrice> previousCountryPrices; - protected CountryPrice currentCarbonPrice; - protected CountryPrice currentTimberPrice; - protected Timestep currentTimestep; - protected Map<CommodityType, Map<CropType, Double>> currentDemandFract; - protected double currentGen2EcDemand; - protected Map<WoodType, Double> currentWoodDemand; - protected double exportTaxRate; - - public AbstractCountryAgent(AbstractDemandManager demandManager,CompositeCountry country, Map<CropType, Double> tradeBarriers) { - - this.demandManager = demandManager; - this.country = country; - this.tradeBarriers = tradeBarriers; - } - - public CompositeCountry getCountry() { - return country; - } - - protected void setCurrentTimestep(Timestep timestep) { - currentTimestep = timestep; - } - - private void calculateCountryPrices(GlobalPrice carbonPrice, GlobalPrice timberPrice){ - Map<CropType, CountryPrice> countryPrices = new HashMap <CropType, CountryPrice>(); - - for (CropType c : CropType.getImportedTypes()) { - GlobalPrice worldPrice = currentWorldPrices.get(c); - CountryPrice prices = createCountryPrices(c, worldPrice); - countryPrices.put(c, prices); - } - - currentCountryPrices = countryPrices; - currentCarbonPrice = new CountryPrice(carbonPrice, 0); // no trade barriers - currentTimberPrice = new CountryPrice(timberPrice, ModelConfig.WOOD_TRADE_BARRIER); - } - - protected void savePreviousProducerCropPrices() { - previousCountryPrices = currentCountryPrices; - } - - protected void calculateExportTax() { - exportTaxRate = 0; - - if (previousCountryPrices != null) { - for (CropType crop : CommodityType.CEREALS.getCropTypes()) { - double newPrice = currentCountryPrices.get(crop).getConsumerPrice(); - double oldPrice = previousCountryPrices.get(crop).getConsumerPrice(); - double priceChangeRate = (newPrice - oldPrice)/ oldPrice; - if (priceChangeRate > ModelConfig.EXPORT_TAX_THRESHOLD) { - exportTaxRate = ModelConfig.EXPORT_TAX_RATE; - LogWriter.println(String.format("\ncalculateExportTax: Price Spike, %s, %d, %s, %.6f, %.6f, %.2f\n", country, currentTimestep.getTimestep(), crop, oldPrice, newPrice, priceChangeRate*100)); - break; - } - else - LogWriter.println(String.format("calculateExportTax: Price change below threshold, %s, %d, %s, %.6f, %.6f, %.2f", country, currentTimestep.getTimestep(), crop, oldPrice, newPrice, priceChangeRate*100)); - } - } - - LogWriter.println(String.format("calculateExportTax %s: exportTax is now %s", country, exportTaxRate)); - } - - abstract protected CountryPrice createCountryPrices(CropType crop, GlobalPrice worldPrice); - - protected void calculateCountryPricesAndDemand(Map<CropType, GlobalPrice> worldPrices, GlobalPrice carbonPrice, GlobalPrice timberPrice, boolean outputGamsDemand) { - LogWriter.println("\ncalculateCountryPricesAndDemand for " + country + " "+ currentTimestep.getTimestep()); - currentWorldPrices = worldPrices; - currentDemandFract = getDemandFraction(); - calculateCountryPrices(carbonPrice, timberPrice); - - Map<CommodityType, Double> producerPrices = getProducerCommodityPrices(); - currentProjectedDemand = demandManager.getDemand(country, currentTimestep.getYear(), producerPrices, outputGamsDemand); - currentGen2EcDemand = demandManager.getSecondGenBioenergyDemand(country, currentTimestep.getYear()); - currentWoodDemand = demandManager.getWoodDemandComposite(country, currentTimestep.getYear()); - } - - private Map<CommodityType, Double> getProducerCommodityPrices() { - if (!ModelConfig.PRICE_ELASTIC_DEMAND) - return null; - - Map<CommodityType, Double> prices = new HashMap<CommodityType, Double>(); - - for (CommodityType commodity : CommodityType.getAllFoodItems()) { - double commPricePlum = getCommPriceFromCropPrice(commodity); - prices.put(commodity, commPricePlum); - LogWriter.println("Producer price for " + commodity.getGamsName() + " is " + commPricePlum); - } - - return prices; - } - - protected abstract double getCommPriceFromCropPrice(CommodityType commodity); - - public Map<CommodityType, Double> getCurrentProjectedDemand() { - return currentProjectedDemand; - } - - public Map<CropType, CountryPrice> getCurrentCountryPrices() { - return currentCountryPrices; - } - - private Map<CommodityType, Map<CropType, Double>> getDemandFraction() { - Map<CommodityType, Map<CropType, Double>> baseDemandFact = demandManager.getBaseDemandFracts(country); - Map<CommodityType, Map<CropType, Double>> demandFraction = new HashMap<CommodityType, Map<CropType, Double>>(); - - for (Map.Entry<CommodityType, Map<CropType, Double>> entry : baseDemandFact.entrySet()) { - CommodityType comm = entry.getKey(); - Map<CropType, Double> baseFracts = entry.getValue(); - - if (ModelConfig.IS_CALIBRATION_RUN || !ModelConfig.DEMAND_FRACT_BY_COST || (comm != CommodityType.CEREALS && comm != CommodityType.OILCROPSPULSES)) - demandFraction.put(comm, baseFracts); - - else { - Map<CropType, Double> newFracts = new HashMap<CropType, Double>(); - double totalAdjQuantities = 0; - - for (Map.Entry<CropType, Double> cropEntry : baseFracts.entrySet()) { - GlobalPrice worldPrice = currentWorldPrices.get(cropEntry.getKey()); - double adjFract = worldPrice.getReferencePrice() / worldPrice.getExportPrice() * cropEntry.getValue(); // adjust by price ratio - newFracts.put(cropEntry.getKey(), adjFract); - totalAdjQuantities += adjFract; - } - - Map<CropType, Double> cropFracts = new HashMap<CropType, Double>(); - for (Map.Entry<CropType, Double> cropEntry : newFracts.entrySet()) - cropFracts.put(cropEntry.getKey(), cropEntry.getValue()/totalAdjQuantities); //rebase to 1 by dividing by totalAdjQuantities - - demandFraction.put(comm, cropFracts); - } - } - - return demandFraction; - } - - protected void updateNetImportsFromProdAndDemand(Map<CommodityType, Double> demands, Map<CommodityType, Map<CropType, Double>> minDemandFracts, Map<CropType, CropUsageData> cropUsages) { - LogWriter.println("AbstractCountryAgent: updateNetImportsFromProdAndDemand for " + country); - - for (CommodityType commodity : CommodityType.getAllFoodItems()) { - - if (commodity != CommodityType.CEREALS && commodity != CommodityType.OILCROPSPULSES && commodity.getCropTypes().size() != 1) - throw new RuntimeException("Not cereal or oilcropspulses and not 1 to 1 mapping for commodity to crop: " + commodity); // skips cereals which is a special case and handled separately below - - double demand = demands.get(commodity); - if (commodity == CommodityType.CEREALS || commodity == CommodityType.OILCROPSPULSES) { - Map<CropType, Double> netImportsFromMinDemands = new HashMap<CropType, Double>(); - double totalProd = 0, totalmportFromMD = 0, totalExcessProd = 0; - - for (CropType crop : commodity.getCropTypes()) { - CropUsageData cropUsage = cropUsages.get(crop); - double prod = cropUsage.getProductionExpected() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed(); - totalProd += prod; - double minFract = minDemandFracts.get(commodity).containsKey(crop) ? minDemandFracts.get(commodity).get(crop) : 0.0; - double netImportsFromMinDemand = minFract * demand - prod; - netImportsFromMinDemands.put(crop, netImportsFromMinDemand); - - if (netImportsFromMinDemand > 0) - totalmportFromMD += netImportsFromMinDemand; - else - totalExcessProd += netImportsFromMinDemand; - } - double additionalNetImportsRequired = demand - totalProd - totalmportFromMD; - LogWriter.println("additionalNetImportsRequired is " + additionalNetImportsRequired); - - for (CropType crop : commodity.getCropTypes()) { - double netImportsMD = netImportsFromMinDemands.get(crop); - double netImports=0; - if (additionalNetImportsRequired > 0) { // overall need to import more - if (minDemandFracts.get(commodity).containsKey(crop)) - netImports = netImportsMD + minDemandFracts.get(commodity).get(crop) * additionalNetImportsRequired; // divide required additional imports by minCerealFracts - } - else { // overall need to export more - if (netImportsMD > 0) - netImports = netImportsMD; // still import what we need for minimum fraction - else if(netImportsMD < 0) - netImports = netImportsMD / totalExcessProd * additionalNetImportsRequired; // divide exports by production in excess of minimum demand - } - - CropUsageData cropUsage = cropUsages.get(crop); - LogWriter.println("Updating " + commodity + " net imports " + crop + " to " + netImports); - cropUsage.updateNetImports(netImports); - } - } - else { - // simple 1-1 commodity to cereal mappings - for (CropType crop : commodity.getCropTypes()) { - CropUsageData cropUsage = cropUsages.get(crop); - double prod = cropUsage.getProductionExpected() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed(); - double netImports = demand - prod; - LogWriter.println("Updating net imports " + crop + " to " + netImports); - cropUsage.updateNetImports(netImports); - } - } - } - } - - abstract public Map<CropType, CropUsageData> getCropUsageData(); - - public double getCurrentGen2EcDemand() { - return currentGen2EcDemand; - } - - abstract public double getNetCarbonFlux(); - - abstract public Map<WoodType, WoodUsageData> getWoodUsageData(); +package ac.ed.lurg.country; + +import java.util.HashMap; +import java.util.Map; + +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 { + + protected AbstractDemandManager demandManager; + protected CompositeCountry country; + protected Map<CropType, Double> tradeBarriers; + protected Map<CommodityType, Double> currentProjectedDemand; + private Map<CropType, GlobalPrice> currentWorldPrices; + protected Map<CropType, CountryPrice> currentCountryPrices; + private Map<CropType, CountryPrice> previousCountryPrices; + protected CountryPrice currentCarbonPrice; + protected CountryPrice currentTimberPrice; + protected Timestep currentTimestep; + protected Map<CommodityType, Map<CropType, Double>> currentDemandFract; + protected double currentGen2EcDemand; + protected Map<WoodType, Double> currentWoodDemand; + protected double exportTaxRate; + + public AbstractCountryAgent(AbstractDemandManager demandManager,CompositeCountry country, Map<CropType, Double> tradeBarriers) { + + this.demandManager = demandManager; + this.country = country; + this.tradeBarriers = tradeBarriers; + } + + public CompositeCountry getCountry() { + return country; + } + + protected void setCurrentTimestep(Timestep timestep) { + currentTimestep = timestep; + } + + private void calculateCountryPrices(GlobalPrice carbonPrice, GlobalPrice timberPrice){ + Map<CropType, CountryPrice> countryPrices = new HashMap <CropType, CountryPrice>(); + + for (CropType c : CropType.getImportedTypes()) { + GlobalPrice worldPrice = currentWorldPrices.get(c); + CountryPrice prices = createCountryPrices(c, worldPrice); + countryPrices.put(c, prices); + } + + currentCountryPrices = countryPrices; + currentCarbonPrice = new CountryPrice(carbonPrice, 0); // no trade barriers + currentTimberPrice = new CountryPrice(timberPrice, ModelConfig.WOOD_TRADE_BARRIER); + } + + protected void savePreviousProducerCropPrices() { + previousCountryPrices = currentCountryPrices; + } + + protected void calculateExportTax() { + exportTaxRate = 0; + + if (previousCountryPrices != null) { + for (CropType crop : CommodityType.CEREALS.getCropTypes()) { + double newPrice = currentCountryPrices.get(crop).getConsumerPrice(); + double oldPrice = previousCountryPrices.get(crop).getConsumerPrice(); + double priceChangeRate = (newPrice - oldPrice)/ oldPrice; + if (priceChangeRate > ModelConfig.EXPORT_TAX_THRESHOLD) { + exportTaxRate = ModelConfig.EXPORT_TAX_RATE; + LogWriter.println(String.format("\ncalculateExportTax: Price Spike, %s, %d, %s, %.6f, %.6f, %.2f\n", country, currentTimestep.getTimestep(), crop, oldPrice, newPrice, priceChangeRate*100)); + break; + } + else + LogWriter.println(String.format("calculateExportTax: Price change below threshold, %s, %d, %s, %.6f, %.6f, %.2f", country, currentTimestep.getTimestep(), crop, oldPrice, newPrice, priceChangeRate*100)); + } + } + + LogWriter.println(String.format("calculateExportTax %s: exportTax is now %s", country, exportTaxRate)); + } + + abstract protected CountryPrice createCountryPrices(CropType crop, GlobalPrice worldPrice); + + protected void calculateCountryPricesAndDemand(Map<CropType, GlobalPrice> worldPrices, GlobalPrice carbonPrice, GlobalPrice timberPrice, boolean outputGamsDemand) { + LogWriter.println("\ncalculateCountryPricesAndDemand for " + country + " "+ currentTimestep.getTimestep()); + currentWorldPrices = worldPrices; + currentDemandFract = getDemandFraction(); + calculateCountryPrices(carbonPrice, timberPrice); + + Map<CommodityType, Double> producerPrices = getProducerCommodityPrices(); + currentProjectedDemand = demandManager.getDemand(country, currentTimestep.getYear(), producerPrices, outputGamsDemand); + currentGen2EcDemand = demandManager.getSecondGenBioenergyDemand(country, currentTimestep.getYear()); + currentWoodDemand = demandManager.getWoodDemandComposite(country, currentTimestep.getYear()); + } + + private Map<CommodityType, Double> getProducerCommodityPrices() { + if (!ModelConfig.PRICE_ELASTIC_DEMAND) + return null; + + Map<CommodityType, Double> prices = new HashMap<CommodityType, Double>(); + + for (CommodityType commodity : CommodityType.getAllFoodItems()) { + double commPricePlum = getCommPriceFromCropPrice(commodity); + prices.put(commodity, commPricePlum); + LogWriter.println("Producer price for " + commodity.getGamsName() + " is " + commPricePlum); + } + + return prices; + } + + protected abstract double getCommPriceFromCropPrice(CommodityType commodity); + + public Map<CommodityType, Double> getCurrentProjectedDemand() { + return currentProjectedDemand; + } + + public Map<CropType, CountryPrice> getCurrentCountryPrices() { + return currentCountryPrices; + } + + private Map<CommodityType, Map<CropType, Double>> getDemandFraction() { + Map<CommodityType, Map<CropType, Double>> baseDemandFact = demandManager.getBaseDemandFracts(country); + Map<CommodityType, Map<CropType, Double>> demandFraction = new HashMap<CommodityType, Map<CropType, Double>>(); + + for (Map.Entry<CommodityType, Map<CropType, Double>> entry : baseDemandFact.entrySet()) { + CommodityType comm = entry.getKey(); + Map<CropType, Double> baseFracts = entry.getValue(); + + if (ModelConfig.IS_CALIBRATION_RUN || !ModelConfig.DEMAND_FRACT_BY_COST || (comm != CommodityType.CEREALS && comm != CommodityType.OILCROPSPULSES)) + demandFraction.put(comm, baseFracts); + + else { + Map<CropType, Double> newFracts = new HashMap<CropType, Double>(); + double totalAdjQuantities = 0; + + for (Map.Entry<CropType, Double> cropEntry : baseFracts.entrySet()) { + GlobalPrice worldPrice = currentWorldPrices.get(cropEntry.getKey()); + double adjFract = worldPrice.getReferencePrice() / worldPrice.getExportPrice() * cropEntry.getValue(); // adjust by price ratio + newFracts.put(cropEntry.getKey(), adjFract); + totalAdjQuantities += adjFract; + } + + Map<CropType, Double> cropFracts = new HashMap<CropType, Double>(); + for (Map.Entry<CropType, Double> cropEntry : newFracts.entrySet()) + cropFracts.put(cropEntry.getKey(), cropEntry.getValue()/totalAdjQuantities); //rebase to 1 by dividing by totalAdjQuantities + + demandFraction.put(comm, cropFracts); + } + } + + return demandFraction; + } + + protected void updateNetImportsFromProdAndDemand(Map<CommodityType, Double> demands, Map<CommodityType, Map<CropType, Double>> minDemandFracts, Map<CropType, CropUsageData> cropUsages) { + LogWriter.println("AbstractCountryAgent: updateNetImportsFromProdAndDemand for " + country); + + for (CommodityType commodity : CommodityType.getAllFoodItems()) { + + if (commodity != CommodityType.CEREALS && commodity != CommodityType.OILCROPSPULSES && commodity.getCropTypes().size() != 1) + throw new RuntimeException("Not cereal or oilcropspulses and not 1 to 1 mapping for commodity to crop: " + commodity); // skips cereals which is a special case and handled separately below + + double demand = demands.get(commodity); + if (commodity == CommodityType.CEREALS || commodity == CommodityType.OILCROPSPULSES) { + Map<CropType, Double> netImportsFromMinDemands = new HashMap<CropType, Double>(); + double totalProd = 0, totalmportFromMD = 0, totalExcessProd = 0; + + for (CropType crop : commodity.getCropTypes()) { + CropUsageData cropUsage = cropUsages.get(crop); + double prod = cropUsage.getProductionExpected() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed(); + totalProd += prod; + double minFract = minDemandFracts.get(commodity).containsKey(crop) ? minDemandFracts.get(commodity).get(crop) : 0.0; + double netImportsFromMinDemand = minFract * demand - prod; + netImportsFromMinDemands.put(crop, netImportsFromMinDemand); + + if (netImportsFromMinDemand > 0) + totalmportFromMD += netImportsFromMinDemand; + else + totalExcessProd += netImportsFromMinDemand; + } + double additionalNetImportsRequired = demand - totalProd - totalmportFromMD; + LogWriter.println("additionalNetImportsRequired is " + additionalNetImportsRequired); + + for (CropType crop : commodity.getCropTypes()) { + double netImportsMD = netImportsFromMinDemands.get(crop); + double netImports=0; + if (additionalNetImportsRequired > 0) { // overall need to import more + if (minDemandFracts.get(commodity).containsKey(crop)) + netImports = netImportsMD + minDemandFracts.get(commodity).get(crop) * additionalNetImportsRequired; // divide required additional imports by minCerealFracts + } + else { // overall need to export more + if (netImportsMD > 0) + netImports = netImportsMD; // still import what we need for minimum fraction + else if(netImportsMD < 0) + netImports = netImportsMD / totalExcessProd * additionalNetImportsRequired; // divide exports by production in excess of minimum demand + } + + CropUsageData cropUsage = cropUsages.get(crop); + LogWriter.println("Updating " + commodity + " net imports " + crop + " to " + netImports); + cropUsage.updateNetImports(netImports); + } + } + else { + // simple 1-1 commodity to cereal mappings + for (CropType crop : commodity.getCropTypes()) { + CropUsageData cropUsage = cropUsages.get(crop); + double prod = cropUsage.getProductionExpected() *(1-crop.getSeedAndWasteRate()) - cropUsage.getMonogastricFeed() - cropUsage.getRuminantFeed(); + double netImports = demand - prod; + LogWriter.println("Updating net imports " + crop + " to " + netImports); + cropUsage.updateNetImports(netImports); + } + } + } + } + + abstract public Map<CropType, CropUsageData> getCropUsageData(); + + public double getCurrentGen2EcDemand() { + return currentGen2EcDemand; + } + + abstract public double getNetCarbonFlux(); + + abstract public Map<WoodType, WoodUsageData> getWoodUsageData(); + + public CountryPrice getCurrentCountryWoodPrice() { + return currentTimberPrice; + } + + public CountryPrice getCurrentCountryCarbonPrice() { + return currentCarbonPrice; + } } \ 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 09bb7019651787fc3bd508fa7db017dfe685e6b5..ac756610f4120cc9f3197cc6b49d3f224f5f9af4 100644 --- a/src/ac/ed/lurg/country/CountryAgent.java +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -1,352 +1,353 @@ -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; -import java.util.Map; -import java.util.Map.Entry; -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.carbon.CarbonFluxItem; -import ac.ed.lurg.country.gams.GamsCountryInput; -import ac.ed.lurg.country.gams.GamsRasterInput; -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.landuse.CropUsageData; -import ac.ed.lurg.landuse.IrrigationItem; -import ac.ed.lurg.landuse.LandUseItem; -import ac.ed.lurg.landuse.LccKey; -import ac.ed.lurg.landuse.WoodUsageData; -import ac.ed.lurg.types.CommodityType; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.WoodType; -import ac.ed.lurg.utils.LogWriter; -import ac.ed.lurg.utils.cluster.Cluster; -import ac.ed.lurg.utils.cluster.KMeans; -import ac.ed.lurg.yield.YieldClusterPoint; -import ac.ed.lurg.yield.YieldRaster; -import ac.ed.lurg.yield.YieldResponsesItem; -import ac.sac.raster.IntegerRasterItem; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -public class CountryAgent extends AbstractCountryAgent { - - private GamsRasterOutput previousGamsRasterOutput; - private RasterSet<IntegerRasterItem> yieldClusters; - private Map<CropType, Double> subsidyRates; - 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) { - - super(demandManager, country, tradeBarriers); - this.yieldClusters = yieldClusters; - this.subsidyRates = subsidyRates; - - 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() { - return yieldClusters; - } - - private RasterSet<IntegerRasterItem> calcYieldClusters(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces) { - - LogWriter.println("calcYieldClusters: for " + ModelConfig.NUM_YIELD_CLUSTERS + " clusters"); - - // create collection of ClusteringPoints from countryYieldSurfaces, these have the RasterKey and data for yield (or access to them) - HashSet<YieldClusterPoint> clusteringPoints = new HashSet<YieldClusterPoint>(); - for (Entry<RasterKey, YieldResponsesItem> entry : countryYieldSurfaces.entrySet()) { - YieldResponsesItem yieldresp = entry.getValue(); - if (yieldresp != null) { - RasterKey key = entry.getKey(); - IrrigationItem irrigItem = irrigData.get(key); - - LandUseItem luItem = previousGamsRasterOutput.getLandUses().get(key); - double totalLand = luItem.getTotalLandCoverArea()-luItem.getLandCoverArea(LandCoverType.URBAN); - double protectedAreaFrac = (totalLand <= 0) ? 0.0 : luItem.getTotalProtectedArea() / totalLand; - - clusteringPoints.add(new YieldClusterPoint(key, yieldresp, irrigItem, protectedAreaFrac)); - } - } - - - - // do the clustering - KMeans<String, YieldClusterPoint> kmeans = new KMeans<String, YieldClusterPoint>(clusteringPoints, ModelConfig.NUM_YIELD_CLUSTERS); - kmeans.calculateClusters(100, 0.1); - kmeans.printClusters(); - - // reformat output - List<Cluster<String, YieldClusterPoint>> yieldClusters = kmeans.getClusters(); - RasterSet<IntegerRasterItem> mapping = new RasterSet<IntegerRasterItem>(countryYieldSurfaces.getHeaderDetails()); - - int id = 1; - for (Cluster<String, YieldClusterPoint> c : yieldClusters) { - for (YieldClusterPoint p : c.getPoints()) - mapping.put(p.getRasterKey(), new IntegerRasterItem(id)); - - if (c.getPoints().size()>0) - id++; - } - - return mapping; - } - - public GamsRasterOutput determineProduction(YieldRaster countryYieldSurfaces, RasterSet<IrrigationItem> irrigData, - Map<CropType, GlobalPrice> worldPrices, RasterSet<CarbonFluxItem> carbonFluxData, - RasterSet<WoodYieldItem> woodYieldData, Map<LccKey, Double> conversionCosts, - double carbonDemandIncrease, GlobalPrice carbonPrice, GlobalPrice woodPrice) { - - // project demand - calculateCountryPricesAndDemand(worldPrices, carbonPrice, woodPrice, false); - - if (ModelConfig.APPLY_EXPORT_TAXES && !ModelConfig.IS_CALIBRATION_RUN) { - calculateExportTax(); - savePreviousProducerCropPrices(); - } - - if (saveGamsGdxFiles && ModelConfig.PRICE_ELASTIC_DEMAND) - saveGDXFile("demand"); - - if (currentProjectedDemand.size() == 0) { - LogWriter.printlnError("No demand for country " + country + " so skipping it"); - } - else if (countryYieldSurfaces.size() == 0 ) { - LogWriter.printlnError("No yield values for " + country + " so skipping it"); - } - else if (ModelConfig.DEBUG_JUST_DEMAND_OUTPUT) { // if this debug flag is set we don't do the optimisation - } - else { - if (yieldClusters==null) { - yieldClusters = calcYieldClusters(irrigData, countryYieldSurfaces); // this should only be on the first timestep - } - - // optimize areas and intensity - GamsRasterInput input = getGamsRasterInput(irrigData, countryYieldSurfaces, woodYieldData, carbonFluxData, conversionCosts, - carbonDemandIncrease); - GamsRasterOptimiser opti = new GamsRasterOptimiser(input, yieldClusters); - LogWriter.println("Running " + country.getName() + ", currentTimestep " + currentTimestep); - - GamsRasterOutput result = opti.run(); - - if (saveGamsGdxFiles) - saveGDXFile("landuse"); - - previousGamsRasterOutput = result; - - incrementLandCoverAge(); - - return result; - } - - throw new RuntimeException("Skipping optimisation of country " + country); - } - - public void recalculateDemandAndNetImports(Map<CropType, GlobalPrice> worldPrices, GlobalPrice carbonPrice, GlobalPrice timberPrice) { - shockGDP(); - calculateCountryPricesAndDemand(worldPrices, carbonPrice, timberPrice, true); - 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) { - double allowedImportChange; - - if (currentTimestep.isInitialTimestep() || (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION)) { // initialisation time-step - allowedImportChange = 0.0; - } - 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. - } - - Map<CropType, TradeConstraint> importConstraints = new HashMap<CropType, TradeConstraint>(); - - for (Map.Entry<CropType, CropUsageData> entry : previousGamsRasterOutput.getCropUsageData().entrySet()) { - CropUsageData cropUsage = entry.getValue(); - CropType crop = entry.getKey(); - double baseTrade = cropUsage.getNetImportsExpected(); - double changeUp = 0.0; - double changeDown = 0.0; - - if (allowedImportChange > 0.0) { - double maxOfProdOrSupply = cropUsage.getProductionExpected() + Math.max(baseTrade, 0); //max of supply for food - - // max of supply overall, needed for when imports are supplying feed and change is zero to allow for production to replace imports - // CONFUSING NOT SURE IF NEEDED if (maxOfProdOrSupply == 0) maxOfProdOrSupply = Math.max(cropUsage.getNetImports() + cropUsage.getProduction() , cropUsage.getProduction()); - - changeDown = changeUp = allowedImportChange * maxOfProdOrSupply; - } - - if (CropType.ENERGY_CROPS.equals(crop) && baseTrade == 0) { // could apply this logic for all crops? - changeDown = changeUp = allowedImportChange * currentGen2EcDemand; - } - - importConstraints.put(crop, new TradeConstraint(baseTrade - changeDown, baseTrade + changeUp)); - } - - // Carbon import/export constraints TODO not used, might want in future - TradeConstraint carbonTradeConstraint; - { - double baseTrade = getNetCarbonFlux(); - double countryArea = LandUseItem.getTotalLandArea(previousGamsRasterOutput.getLandUses().values()); - double change = 0.0; - if (allowedImportChange > 0.0) - change = Math.max(baseTrade * allowedImportChange, carbonDemandIncrease * countryArea / 1500 * 2); - carbonTradeConstraint = new TradeConstraint(baseTrade - change, baseTrade + change); - } - - // Timber import/export constraints - Map<WoodType, TradeConstraint> woodTradeConstraints = new HashMap<WoodType, TradeConstraint>(); - - for (Map.Entry<WoodType, WoodUsageData> entry : previousGamsRasterOutput.getWoodUsageData().entrySet()) { - WoodType woodType = entry.getKey(); - if (!ModelConfig.IS_FORESTRY_ON) { - woodTradeConstraints.put(woodType, new TradeConstraint(0, 0)); // No exports or imports if forestry off - continue; - } - WoodUsageData woodUsage = entry.getValue(); - double baseTrade = woodUsage.getNetImport(); - double changeUp = 0.0; - double changeDown = 0.0; - if (allowedImportChange > 0.0) { - double maxOfProdOrSupply = woodUsage.getHarvest() + Math.max(baseTrade, 0); - changeUp = changeDown = allowedImportChange * maxOfProdOrSupply; - } - changeUp = changeDown = currentWoodDemand.values().stream().reduce(0.0, Double::sum); // TODO temp - woodTradeConstraints.put(woodType, new TradeConstraint(baseTrade - changeDown, baseTrade + changeUp)); - } - - GamsCountryInput countryLevelInputs = new GamsCountryInput(country, currentProjectedDemand, currentCountryPrices, importConstraints, - previousGamsRasterOutput.getCropUsageData(), currentDemandFract, subsidyRates, currentCarbonPrice, carbonTradeConstraint, currentWoodDemand, - currentTimberPrice, woodTradeConstraints, previousGamsRasterOutput.getWoodUsageData(), harvestWoodFromRotation(woodYieldData)); - GamsRasterInput input = new GamsRasterInput(currentTimestep, countryYieldSurfaces, previousGamsRasterOutput.getLandUses(), irrigData, - woodYieldData, carbonFluxData, conversionCosts, countryLevelInputs); - - return input; - } - - @Override - protected double getCommPriceFromCropPrice(CommodityType commodity) { - double commPricePlum = 0; - Map<CropType, Double> demandFract = currentDemandFract.get(commodity); - - for (CropType crop : commodity.getCropTypes()) { - double consumerCropPrice = currentCountryPrices.get(crop).getConsumerPrice(); - commPricePlum += consumerCropPrice * demandFract.get(crop); // weight price by base demand of each cereal crop - } - - return commPricePlum; - } - - @Override - protected CountryPrice createCountryPrices(CropType crop, GlobalPrice worldPrice) { - Map<CropType, CropUsageData> cropUsageMap = previousGamsRasterOutput.getCropUsageData(); - CropUsageData cropUsageData = cropUsageMap.get(crop); - - double weighting = Math.max(0, cropUsageData.getShockedNetImports()/(cropUsageData.getProductionExpected()+cropUsageData.getShockedNetImports())); - double prodCost = cropUsageData.getProdCostRate(); - Double tb = tradeBarriers.get(crop); - double tradeBarrier = tb==null ? 0 : tb; - double exportTaxRateForCrop = CommodityType.CEREALS.getCropTypes().contains(crop) ? exportTaxRate : 0.0; - CountryPrice cp = new CountryPrice(crop, worldPrice, tradeBarrier, prodCost, weighting, exportTaxRateForCrop); - return cp; - } - - public void shockGDP() { - - double totalProduction = getTotalProduction(); - double shockMagnitude = 0.0; - - for (Map.Entry<CropType, CropUsageData> entry : previousGamsRasterOutput.getCropUsageData().entrySet()) { - CropUsageData cropUsage = entry.getValue(); - CropType crop = entry.getKey(); - if(crop.isImportedCrop() && cropUsage.getProductionExpected() != 0) - shockMagnitude += (cropUsage.getProductionShock() /cropUsage.getProductionExpected()) * (cropUsage.getProductionExpected()/totalProduction); - } - demandManager.updateGdpLossesFromShock(country, currentTimestep.getYear(), shockMagnitude); - } - - public RasterSet<LandUseItem> getLandUses() { - return previousGamsRasterOutput.getLandUses(); - } - - public Map<CropType, CropUsageData> getCropUsageData() { - return previousGamsRasterOutput.getCropUsageData(); - } - - public double getTotalProduction() { - double totalProduction = 0; - - for (Map.Entry<CropType, CropUsageData> entry : previousGamsRasterOutput.getCropUsageData().entrySet()) { - CropUsageData cropUsage = entry.getValue(); - CropType crop = entry.getKey(); - if(crop.isImportedCrop()) //assuming pasture and set aside not part of agricultural value, valid or not? - totalProduction += cropUsage.getProductionExpected(); - } - return totalProduction; - } - - public void updateProtectedAreas(RasterSet<LandUseItem> newProtectedAreas) { - for (Entry<RasterKey, LandUseItem> entry : previousGamsRasterOutput.getLandUses().entrySet()) { - double updatedProtectedFract = newProtectedAreas.get(entry.getKey()).getProtectedFraction(); - entry.getValue().setProtectedFraction(updatedProtectedFract); - } - - } - - private double harvestWoodFromRotation(RasterSet<WoodYieldItem> woodYieldData) { - double totalHarvest = 0; - for (RasterKey key : previousGamsRasterOutput.getLandUses().keySet()) { - WoodYieldItem wyItem = woodYieldData.get(key); - if (wyItem == null) - continue; // TODO Deal with this properly - totalHarvest += wyItem.getCurrentRotationHarvest(); - } - return totalHarvest; - } - - private void incrementLandCoverAge() { - for (LandUseItem luItem : previousGamsRasterOutput.getLandUses().values()) { - luItem.incrementTilesAge(); - } - } - - public double getNetCarbonFlux() { - return previousGamsRasterOutput.getNetCarbonFlux(); - } - - public Map<WoodType, WoodUsageData> getWoodUsageData() { - return previousGamsRasterOutput.getWoodUsageData(); - } - +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; +import java.util.Map; +import java.util.Map.Entry; +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.carbon.CarbonFluxItem; +import ac.ed.lurg.country.gams.GamsCountryInput; +import ac.ed.lurg.country.gams.GamsRasterInput; +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.landuse.CropUsageData; +import ac.ed.lurg.landuse.IrrigationItem; +import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.LccKey; +import ac.ed.lurg.landuse.WoodUsageData; +import ac.ed.lurg.types.CommodityType; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.ed.lurg.types.WoodType; +import ac.ed.lurg.utils.LogWriter; +import ac.ed.lurg.utils.cluster.Cluster; +import ac.ed.lurg.utils.cluster.KMeans; +import ac.ed.lurg.yield.YieldClusterPoint; +import ac.ed.lurg.yield.YieldRaster; +import ac.ed.lurg.yield.YieldResponsesItem; +import ac.sac.raster.IntegerRasterItem; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterSet; + +public class CountryAgent extends AbstractCountryAgent { + + private GamsRasterOutput previousGamsRasterOutput; + private RasterSet<IntegerRasterItem> yieldClusters; + private Map<CropType, Double> subsidyRates; + 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) { + + super(demandManager, country, tradeBarriers); + this.yieldClusters = yieldClusters; + this.subsidyRates = subsidyRates; + + 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() { + return yieldClusters; + } + + private RasterSet<IntegerRasterItem> calcYieldClusters(RasterSet<IrrigationItem> irrigData, YieldRaster countryYieldSurfaces) { + + LogWriter.println("calcYieldClusters: for " + ModelConfig.NUM_YIELD_CLUSTERS + " clusters"); + + // create collection of ClusteringPoints from countryYieldSurfaces, these have the RasterKey and data for yield (or access to them) + HashSet<YieldClusterPoint> clusteringPoints = new HashSet<YieldClusterPoint>(); + for (Entry<RasterKey, YieldResponsesItem> entry : countryYieldSurfaces.entrySet()) { + YieldResponsesItem yieldresp = entry.getValue(); + if (yieldresp != null) { + RasterKey key = entry.getKey(); + IrrigationItem irrigItem = irrigData.get(key); + + LandUseItem luItem = previousGamsRasterOutput.getLandUses().get(key); + double totalLand = luItem.getTotalLandCoverArea()-luItem.getLandCoverArea(LandCoverType.URBAN); + double protectedAreaFrac = (totalLand <= 0) ? 0.0 : luItem.getTotalLandCoverArea(LandProtectionType.PROTECTED) / totalLand; + + clusteringPoints.add(new YieldClusterPoint(key, yieldresp, irrigItem, protectedAreaFrac)); + } + } + + + + // do the clustering + KMeans<String, YieldClusterPoint> kmeans = new KMeans<String, YieldClusterPoint>(clusteringPoints, ModelConfig.NUM_YIELD_CLUSTERS); + kmeans.calculateClusters(100, 0.1); + kmeans.printClusters(); + + // reformat output + List<Cluster<String, YieldClusterPoint>> yieldClusters = kmeans.getClusters(); + RasterSet<IntegerRasterItem> mapping = new RasterSet<IntegerRasterItem>(countryYieldSurfaces.getHeaderDetails()); + + int id = 1; + for (Cluster<String, YieldClusterPoint> c : yieldClusters) { + for (YieldClusterPoint p : c.getPoints()) + mapping.put(p.getRasterKey(), new IntegerRasterItem(id)); + + if (c.getPoints().size()>0) + id++; + } + + return mapping; + } + + public GamsRasterOutput determineProduction(YieldRaster countryYieldSurfaces, RasterSet<IrrigationItem> irrigData, + Map<CropType, GlobalPrice> worldPrices, RasterSet<CarbonFluxItem> carbonFluxData, + RasterSet<WoodYieldItem> woodYieldData, Map<LccKey, Double> conversionCosts, + double carbonDemandIncrease, GlobalPrice carbonPrice, GlobalPrice woodPrice) { + + // project demand + calculateCountryPricesAndDemand(worldPrices, carbonPrice, woodPrice, false); + + if (ModelConfig.APPLY_EXPORT_TAXES && !ModelConfig.IS_CALIBRATION_RUN) { + calculateExportTax(); + savePreviousProducerCropPrices(); + } + + if (saveGamsGdxFiles && ModelConfig.PRICE_ELASTIC_DEMAND) + saveGDXFile("demand"); + + if (currentProjectedDemand.size() == 0) { + LogWriter.printlnError("No demand for country " + country + " so skipping it"); + } + else if (countryYieldSurfaces.size() == 0 ) { + LogWriter.printlnError("No yield values for " + country + " so skipping it"); + } + else if (ModelConfig.DEBUG_JUST_DEMAND_OUTPUT) { // if this debug flag is set we don't do the optimisation + } + else { + if (yieldClusters==null) { + yieldClusters = calcYieldClusters(irrigData, countryYieldSurfaces); // this should only be on the first timestep + } + + // optimize areas and intensity + GamsRasterInput input = getGamsRasterInput(irrigData, countryYieldSurfaces, woodYieldData, carbonFluxData, conversionCosts, + carbonDemandIncrease); + GamsRasterOptimiser opti = new GamsRasterOptimiser(input, yieldClusters); + LogWriter.println("Running " + country.getName() + ", currentTimestep " + currentTimestep); + + GamsRasterOutput result = opti.run(); + + if (saveGamsGdxFiles) + saveGDXFile("landuse"); + + previousGamsRasterOutput = result; + + if (!ModelConfig.IS_CALIBRATION_RUN) + incrementLandCoverAge(); + + return result; + } + + throw new RuntimeException("Skipping optimisation of country " + country); + } + + public void recalculateDemandAndNetImports(Map<CropType, GlobalPrice> worldPrices, GlobalPrice carbonPrice, GlobalPrice timberPrice) { + shockGDP(); + calculateCountryPricesAndDemand(worldPrices, carbonPrice, timberPrice, true); + 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) { + double allowedImportChange; + + if (currentTimestep.isInitialTimestep() || (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.getTimestep() <= ModelConfig.END_FIRST_STAGE_CALIBRATION)) { // initialisation time-step + allowedImportChange = 0.0; + } + 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. + } + + Map<CropType, TradeConstraint> importConstraints = new HashMap<CropType, TradeConstraint>(); + + for (Map.Entry<CropType, CropUsageData> entry : previousGamsRasterOutput.getCropUsageData().entrySet()) { + CropUsageData cropUsage = entry.getValue(); + CropType crop = entry.getKey(); + double baseTrade = cropUsage.getNetImportsExpected(); + double changeUp = 0.0; + double changeDown = 0.0; + + if (allowedImportChange > 0.0) { + double maxOfProdOrSupply = cropUsage.getProductionExpected() + Math.max(baseTrade, 0); //max of supply for food + + // max of supply overall, needed for when imports are supplying feed and change is zero to allow for production to replace imports + // CONFUSING NOT SURE IF NEEDED if (maxOfProdOrSupply == 0) maxOfProdOrSupply = Math.max(cropUsage.getNetImports() + cropUsage.getProduction() , cropUsage.getProduction()); + + changeDown = changeUp = allowedImportChange * maxOfProdOrSupply; + } + + if (CropType.ENERGY_CROPS.equals(crop) && baseTrade == 0) { // could apply this logic for all crops? + changeDown = changeUp = allowedImportChange * currentGen2EcDemand; + } + + importConstraints.put(crop, new TradeConstraint(baseTrade - changeDown, baseTrade + changeUp)); + } + + // Carbon import/export constraints TODO not used, might want in future + TradeConstraint carbonTradeConstraint; + { + double baseTrade = getNetCarbonFlux(); + double countryArea = LandUseItem.getTotalLandArea(previousGamsRasterOutput.getLandUses().values()); + double change = 0.0; + if (allowedImportChange > 0.0) + change = Math.max(baseTrade * allowedImportChange, carbonDemandIncrease * countryArea / 1500 * 2); + carbonTradeConstraint = new TradeConstraint(baseTrade - change, baseTrade + change); + } + + // Timber import/export constraints + Map<WoodType, TradeConstraint> woodTradeConstraints = new HashMap<WoodType, TradeConstraint>(); + + for (Map.Entry<WoodType, WoodUsageData> entry : previousGamsRasterOutput.getWoodUsageData().entrySet()) { + WoodType woodType = entry.getKey(); + if (!ModelConfig.IS_FORESTRY_ON) { + woodTradeConstraints.put(woodType, new TradeConstraint(0, 0)); // No exports or imports if forestry off + continue; + } + WoodUsageData woodUsage = entry.getValue(); + + double baseTrade = woodUsage.getNetImport(); + + // Make sure country can import sufficient wood + if (ModelConfig.IS_CALIBRATION_RUN && currentTimestep.isInitialTimestep()) { + // assume 1tC/ha/year + double potentialYield = LandUseItem.getTotalLandCover(previousGamsRasterOutput.getLandUses().values(), LandCoverType.TIMBER_FOREST); + double currentDemand = currentWoodDemand.get(woodType); + double importsNeeded = Math.max(0, currentDemand - potentialYield * 0.5); + baseTrade = importsNeeded > 0 ? importsNeeded : baseTrade * 2; + } + + double changeUp = 0.0; + double changeDown = 0.0; + if (allowedImportChange > 0.0) { + double maxOfProdOrSupply = woodUsage.getHarvest() + Math.max(baseTrade, 0); + changeUp = changeDown = allowedImportChange * maxOfProdOrSupply; + } + + woodTradeConstraints.put(woodType, new TradeConstraint(baseTrade - changeDown, baseTrade + changeUp)); + } + + GamsCountryInput countryLevelInputs = new GamsCountryInput(country, currentProjectedDemand, currentCountryPrices, importConstraints, + previousGamsRasterOutput.getCropUsageData(), currentDemandFract, subsidyRates, currentCarbonPrice, carbonTradeConstraint, currentWoodDemand, + currentTimberPrice, woodTradeConstraints, previousGamsRasterOutput.getWoodUsageData()); + GamsRasterInput input = new GamsRasterInput(currentTimestep, countryYieldSurfaces, previousGamsRasterOutput.getLandUses(), irrigData, + woodYieldData, carbonFluxData, conversionCosts, countryLevelInputs); + + return input; + } + + @Override + protected double getCommPriceFromCropPrice(CommodityType commodity) { + double commPricePlum = 0; + Map<CropType, Double> demandFract = currentDemandFract.get(commodity); + + for (CropType crop : commodity.getCropTypes()) { + double consumerCropPrice = currentCountryPrices.get(crop).getConsumerPrice(); + commPricePlum += consumerCropPrice * demandFract.get(crop); // weight price by base demand of each cereal crop + } + + return commPricePlum; + } + + @Override + protected CountryPrice createCountryPrices(CropType crop, GlobalPrice worldPrice) { + Map<CropType, CropUsageData> cropUsageMap = previousGamsRasterOutput.getCropUsageData(); + CropUsageData cropUsageData = cropUsageMap.get(crop); + + double weighting = Math.max(0, cropUsageData.getShockedNetImports()/(cropUsageData.getProductionExpected()+cropUsageData.getShockedNetImports())); + double prodCost = cropUsageData.getProdCostRate(); + Double tb = tradeBarriers.get(crop); + double tradeBarrier = tb==null ? 0 : tb; + double exportTaxRateForCrop = CommodityType.CEREALS.getCropTypes().contains(crop) ? exportTaxRate : 0.0; + CountryPrice cp = new CountryPrice(crop, worldPrice, tradeBarrier, prodCost, weighting, exportTaxRateForCrop); + return cp; + } + + public void shockGDP() { + + double totalProduction = getTotalProduction(); + double shockMagnitude = 0.0; + + for (Map.Entry<CropType, CropUsageData> entry : previousGamsRasterOutput.getCropUsageData().entrySet()) { + CropUsageData cropUsage = entry.getValue(); + CropType crop = entry.getKey(); + if(crop.isImportedCrop() && cropUsage.getProductionExpected() != 0) + shockMagnitude += (cropUsage.getProductionShock() /cropUsage.getProductionExpected()) * (cropUsage.getProductionExpected()/totalProduction); + } + demandManager.updateGdpLossesFromShock(country, currentTimestep.getYear(), shockMagnitude); + } + + public RasterSet<LandUseItem> getLandUses() { + return previousGamsRasterOutput.getLandUses(); + } + + public Map<CropType, CropUsageData> getCropUsageData() { + return previousGamsRasterOutput.getCropUsageData(); + } + + public double getTotalProduction() { + double totalProduction = 0; + + for (Map.Entry<CropType, CropUsageData> entry : previousGamsRasterOutput.getCropUsageData().entrySet()) { + CropUsageData cropUsage = entry.getValue(); + CropType crop = entry.getKey(); + if(crop.isImportedCrop()) //assuming pasture and set aside not part of agricultural value, valid or not? + totalProduction += cropUsage.getProductionExpected(); + } + return totalProduction; + } + + public void updateProtectedAreas(RasterSet<LandUseItem> newProtectedAreas) { + for (Entry<RasterKey, LandUseItem> entry : previousGamsRasterOutput.getLandUses().entrySet()) { + double updatedProtectedFract = newProtectedAreas.get(entry.getKey()).getProtectedFraction(); + entry.getValue().setProtectedFraction(updatedProtectedFract); + } + + } + + private void incrementLandCoverAge() { + for (LandUseItem luItem : previousGamsRasterOutput.getLandUses().values()) { + luItem.incrementTilesAge(); + } + } + + public double getNetCarbonFlux() { + return previousGamsRasterOutput.getNetCarbonFlux(); + } + + public Map<WoodType, WoodUsageData> getWoodUsageData() { + return previousGamsRasterOutput.getWoodUsageData(); + } } \ 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 df876f6966afd77ed5d57319e5c02c76d9e8740d..ac5fb9111b8050736c57e653969f019667779412 100644 --- a/src/ac/ed/lurg/country/gams/GamsCountryInput.java +++ b/src/ac/ed/lurg/country/gams/GamsCountryInput.java @@ -1,137 +1,130 @@ -package ac.ed.lurg.country.gams; - -import java.util.HashMap; -import java.util.Map; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.country.CompositeCountry; -import ac.ed.lurg.country.CountryPrice; -import ac.ed.lurg.country.TradeConstraint; -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; - -public class GamsCountryInput { - - private CompositeCountry country; // not really required but useful for debugging - private Map<CommodityType, Double> projectedDemand; - private Map<CropType, TradeConstraint> tradeConstraints; - private Map<CropType, CountryPrice> countryPrices; - private Map<CropType, CropUsageData> previousCropUsageData; - private Map<CommodityType, Map<CropType, Double>> minDemandFractions; - private Map<CropType, Double> subsidyRates; - private CountryPrice carbonPrice; - private TradeConstraint carbonTradeConstraint; - private Map<WoodType, Double> woodDemand; - private CountryPrice woodPrice; - private Map<WoodType, TradeConstraint> woodTradeConstraints; - private Map<WoodType, WoodUsageData> previousWoodUsageData; - private double woodHarvestFromRotation; - - 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, Map<WoodType, Double> woodDemand, - CountryPrice woodPrice, Map<WoodType, TradeConstraint> woodTradeConstraints, Map<WoodType, WoodUsageData> previousWoodUsageData, - double woodHarvestFromRotation) { - super(); - this.country = country; - this.projectedDemand = projectedDemand; - this.tradeConstraints = importConstraints; - this.countryPrices = countryPrices; - this.previousCropUsageData = previousCropUsageData; - this.minDemandFractions = minDemandFracts; - this.subsidyRates = subsidyRates; - this.carbonPrice = carbonPrice; - this.carbonTradeConstraint = carbonTradeConstraint; - this.woodDemand = woodDemand; - this.woodPrice = woodPrice; - this.woodTradeConstraints = woodTradeConstraints; - this.previousWoodUsageData = previousWoodUsageData; - this.woodHarvestFromRotation = woodHarvestFromRotation; - } - - public CompositeCountry getCountry() { - return country; - } - - public Map<CommodityType, Double> getProjectedDemand() { - return projectedDemand; - } - - public Map<CropType, CountryPrice> getCountryPrices() { - return countryPrices; - } - - public Map<CropType, TradeConstraint> getTradeConstraints() { - return tradeConstraints; - } - - public Map<CropType, CropUsageData> getPreviousCropUsageData() { - return previousCropUsageData; - } - - public double getMeatEfficiency() { - - - return ModelConfig.MEAT_EFFICIENCY; // this is already handled by the feed conversion efficiency for each animal product - } - - public Map<CropType, Double> getMinTrade() { - Map<CropType, Double> netImport = new HashMap<CropType, Double>(); - for (Map.Entry<CropType, TradeConstraint> entry : tradeConstraints.entrySet()) { - netImport.put(entry.getKey(), entry.getValue().getMinConstraint()); - } - return netImport; - } - - public Map<CropType, Double> getMaxTrade() { - Map<CropType, Double> netImport = new HashMap<CropType, Double>(); - for (Map.Entry<CropType, TradeConstraint> entry : tradeConstraints.entrySet()) { - netImport.put(entry.getKey(), entry.getValue().getMaxConstraint()); - } - return netImport; - } - - public Map<CommodityType, Map<CropType, Double>> getMinDemandFractions() { - return minDemandFractions; - } - - public Map<CropType, Double> getSubsidyRates() { - return subsidyRates; - } - - public CountryPrice getCarbonPrice() { - return carbonPrice; - } - - public TradeConstraint getCarbonTradeConstraint() { - return carbonTradeConstraint; - } - - public Map<WoodType, Double> getWoodDemand() { - return woodDemand; - } - - public CountryPrice getWoodPrice() { - return woodPrice; - } - - public Map<WoodType, TradeConstraint> getWoodTradeConstraints() { - return woodTradeConstraints; - } - - public Map<WoodType, WoodUsageData> getPreviousWoodUsageData() { - return previousWoodUsageData; - } - - public double getWoodHarvestFromRotation() { - return woodHarvestFromRotation; - } - - public void setTradeConstraints(Map<CropType, TradeConstraint> tradeConstraints) { - this.tradeConstraints = tradeConstraints; - } +package ac.ed.lurg.country.gams; + +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.country.CompositeCountry; +import ac.ed.lurg.country.CountryPrice; +import ac.ed.lurg.country.TradeConstraint; +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; + +public class GamsCountryInput { + + private CompositeCountry country; // not really required but useful for debugging + private Map<CommodityType, Double> projectedDemand; + private Map<CropType, TradeConstraint> tradeConstraints; + private Map<CropType, CountryPrice> countryPrices; + private Map<CropType, CropUsageData> previousCropUsageData; + private Map<CommodityType, Map<CropType, Double>> minDemandFractions; + private Map<CropType, Double> subsidyRates; + private CountryPrice carbonPrice; + private TradeConstraint carbonTradeConstraint; + private Map<WoodType, Double> woodDemand; + private CountryPrice woodPrice; + private Map<WoodType, TradeConstraint> woodTradeConstraints; + private Map<WoodType, WoodUsageData> previousWoodUsageData; + + 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, Map<WoodType, Double> woodDemand, + CountryPrice woodPrice, Map<WoodType, TradeConstraint> woodTradeConstraints, Map<WoodType, WoodUsageData> previousWoodUsageData) { + super(); + this.country = country; + this.projectedDemand = projectedDemand; + this.tradeConstraints = importConstraints; + this.countryPrices = countryPrices; + this.previousCropUsageData = previousCropUsageData; + this.minDemandFractions = minDemandFracts; + this.subsidyRates = subsidyRates; + this.carbonPrice = carbonPrice; + this.carbonTradeConstraint = carbonTradeConstraint; + this.woodDemand = woodDemand; + this.woodPrice = woodPrice; + this.woodTradeConstraints = woodTradeConstraints; + this.previousWoodUsageData = previousWoodUsageData; + } + + public CompositeCountry getCountry() { + return country; + } + + public Map<CommodityType, Double> getProjectedDemand() { + return projectedDemand; + } + + public Map<CropType, CountryPrice> getCountryPrices() { + return countryPrices; + } + + public Map<CropType, TradeConstraint> getTradeConstraints() { + return tradeConstraints; + } + + public Map<CropType, CropUsageData> getPreviousCropUsageData() { + return previousCropUsageData; + } + + public double getMeatEfficiency() { + + + return ModelConfig.MEAT_EFFICIENCY; // this is already handled by the feed conversion efficiency for each animal product + } + + public Map<CropType, Double> getMinTrade() { + Map<CropType, Double> netImport = new HashMap<CropType, Double>(); + for (Map.Entry<CropType, TradeConstraint> entry : tradeConstraints.entrySet()) { + netImport.put(entry.getKey(), entry.getValue().getMinConstraint()); + } + return netImport; + } + + public Map<CropType, Double> getMaxTrade() { + Map<CropType, Double> netImport = new HashMap<CropType, Double>(); + for (Map.Entry<CropType, TradeConstraint> entry : tradeConstraints.entrySet()) { + netImport.put(entry.getKey(), entry.getValue().getMaxConstraint()); + } + return netImport; + } + + public Map<CommodityType, Map<CropType, Double>> getMinDemandFractions() { + return minDemandFractions; + } + + public Map<CropType, Double> getSubsidyRates() { + return subsidyRates; + } + + public CountryPrice getCarbonPrice() { + return carbonPrice; + } + + public TradeConstraint getCarbonTradeConstraint() { + return carbonTradeConstraint; + } + + public Map<WoodType, Double> getWoodDemand() { + return woodDemand; + } + + public CountryPrice getWoodPrice() { + return woodPrice; + } + + public Map<WoodType, TradeConstraint> getWoodTradeConstraints() { + return woodTradeConstraints; + } + + public Map<WoodType, WoodUsageData> getPreviousWoodUsageData() { + return previousWoodUsageData; + } + + public void setTradeConstraints(Map<CropType, TradeConstraint> tradeConstraints) { + this.tradeConstraints = tradeConstraints; + } } \ No newline at end of file diff --git a/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java b/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java index bb42fd0030d9517f47a1aabd2e3482085b5e5a80..798fa03d483cf8a61a565da10ca81a9c012d17e9 100755 --- a/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsDemandOptimiser.java @@ -1,179 +1,179 @@ -package ac.ed.lurg.country.gams; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Vector; - -import com.gams.api.GAMSDatabase; -import com.gams.api.GAMSGlobals; -import com.gams.api.GAMSJob; -import com.gams.api.GAMSOptions; -import com.gams.api.GAMSParameter; -import com.gams.api.GAMSParameterRecord; -import com.gams.api.GAMSVariable; -import com.gams.api.GAMSVariableRecord; -import com.gams.api.GAMSWorkspace; -import com.gams.api.GAMSWorkspaceInfo; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.types.CommodityType; -import ac.ed.lurg.utils.LogWriter; - -public class GamsDemandOptimiser { - - private GamsDemandInput inputData; - - public GamsDemandOptimiser(GamsDemandInput inputData) { - this.inputData = inputData; - } - - public GamsDemandOutput getDemandPc() { - File workingDirectory = new File(ModelConfig.TEMP_DIR); - workingDirectory.mkdir(); - GAMSWorkspaceInfo wsInfo = new GAMSWorkspaceInfo(); - wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath()); - - GAMSWorkspace ws = new GAMSWorkspace(wsInfo); - GAMSOptions opt = ws.addOptions(); - GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.DEMAND_GAMS_MODEL); - - GAMSDatabase inDB = ws.addDatabase(); - setupPriceGdpDB(inDB); - ArrayList<GAMSDatabase> dbs = new ArrayList<GAMSDatabase>(); - dbs.add(inDB); - opt.defines("gdx_prices_and_gdp", inDB.getName()); - - if (ModelConfig.ADJUST_DIET_PREFS) { - LogWriter.println("Adusting diet params"); - GAMSDatabase originalParamDb = ws.addDatabaseFromGDX(new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath()); - GAMSDatabase adjustedParamDb = ws.addDatabase(originalParamDb); - adjustDietParams(adjustedParamDb); - dbs.add(adjustedParamDb); - opt.defines("gdx_parameters", adjustedParamDb.getName()); - } - else - opt.defines("gdx_parameters", new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath()); - - long startTime = System.currentTimeMillis(); - gamsJob.run(opt, dbs.toArray(new GAMSDatabase[dbs.size()])); - - LogWriter.println("Took " + (System.currentTimeMillis() - startTime) + " ms to run"); - return handleResults(gamsJob.OutDB()); - } - - private void adjustDietParams(GAMSDatabase adjustedParamDb) { - // get original parameters - GAMSParameter tauParam = adjustedParamDb.getParameter("tau"); - TauCalculationManager tauManager = TauCalculationManager.getInstance(); - - for (GAMSParameterRecord rec : tauParam) { - String key = rec.getKeys()[0]; - double initialTau = rec.getValue(); - - CommodityType commodity = CommodityType.getForGamsName(key); - double adjusted = tauManager.getFinalTau(inputData.getYear(), initialTau, commodity); - LogWriter.println(String.format("%14s: initialTau=%.6f, adjusted=%.6f", key, initialTau, adjusted)); - rec.setValue(adjusted); - } - } - - private void setupPriceGdpDB(GAMSDatabase inDB) { - GAMSParameter gdpPcP = inDB.addParameter("gdp_pc", 0); - double gdpPc = inputData.getGdpPc(); - LogWriter.println(String.format("gdp_pc = %.5f", gdpPc)); - setGamsParamValue(gdpPcP.addRecord(), gdpPc, 5); - GamsDemandOutput previousGamsDemands = inputData.getPreviousDemands(); - - GAMSParameter prevUP = inDB.addParameter("previousU", 0); - double previousUtility = previousGamsDemands == null ? 0 : previousGamsDemands.getUtility();//-16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc); - LogWriter.println(String.format("previousU = %.5f, estimated = %.5f", previousUtility, -16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc))); - setGamsParamValue(prevUP.addRecord(), previousUtility, 5); - - GAMSParameter maxIncomePropFoodSpendP = inDB.addParameter("maxIncomePropFoodSpend", 0); - setGamsParamValue(maxIncomePropFoodSpendP.addRecord(), ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND, 4); - - LogWriter.println("\nPrice"); - GAMSParameter priceP = inDB.addParameter("price", 1); - GAMSParameter prevSubsP = inDB.addParameter("previousSubs", 1); - GAMSParameter prevDiscP = inDB.addParameter("previousDisc", 1); - - for (Map.Entry<CommodityType, Double> entry : inputData.getPrices().entrySet()) { - CommodityType comm = entry.getKey(); - double priceComm = entry.getValue(); - doCommodity(inDB, priceP, prevSubsP, prevDiscP, gdpPc, previousGamsDemands, comm, priceComm, - comm==CommodityType.NONFOOD ? 5 : 0.05, comm==CommodityType.NONFOOD ? 0.5 : 0.05); - } - } - - private void doCommodity(GAMSDatabase inDB, GAMSParameter priceP, GAMSParameter prevSubsP, GAMSParameter prevDiscP, double gdpPc, - GamsDemandOutput previousGamsDemands, CommodityType comm, double price, double defaultSubs, double defaultDisc) { - - Vector<String> v = new Vector<String>(); - v.add(comm.getGamsName()); - double prevSubs = defaultSubs, prevDisc = defaultDisc; - - if (previousGamsDemands != null) { - GamsCommodityDemand demand = previousGamsDemands.getGamsDemands(comm); - prevSubs = demand.getSubsistence(); - prevDisc = demand.getDiscretionary(); - } - - LogWriter.println(String.format("%14s: price=%.4f, previousSubs=%.4f, previousDisc=%.4f", comm.getGamsName(), price, prevSubs, prevDisc)); - setGamsParamValue(priceP.addRecord(v), price, 4); - setGamsParamValue(prevSubsP.addRecord(v), prevSubs, 4); - setGamsParamValue(prevDiscP.addRecord(v), prevDisc, 4); - } - - private double setGamsParamValue(GAMSParameterRecord param, double val, int places) { - double dOut = Math.round(val * Math.pow(10,places)) / Math.pow(10,places); - param.setValue(dOut); - return dOut; - } - - private GamsDemandOutput handleResults(GAMSDatabase outDB) { - int modelStatus = (int) outDB.getParameter("ms").findRecord().getValue(); - String status = GAMSGlobals.ModelStat.lookup(modelStatus).toString(); - LogWriter.println(String.format("\nDemand %s: Modelstatus (%d) %s, Solvestatus %s", inputData.getCountry(), - modelStatus, status, GAMSGlobals.SolveStat.lookup((int) outDB.getParameter("ss").findRecord().getValue()) )); - - GAMSVariable varU = outDB.getVariable("u"); - double utility = varU.getFirstRecord().getLevel(); - LogWriter.println(String.format("u = %.4f", utility)); - - GAMSVariable varSubs = outDB.getVariable("SubsistenceC"); - GAMSVariable varDisc = outDB.getVariable("DiscretionaryC"); - - HashSet<GamsCommodityDemand> demandMap = new HashSet<GamsCommodityDemand>(); - Collection<CommodityType> unhandledComms = new ArrayList<CommodityType>(CommodityType.getAllFoodItems()); - - for (GAMSVariableRecord rec : varSubs) { - String commodityName = rec.getKeys()[0]; - CommodityType commodity = CommodityType.getForGamsName(commodityName, true); - - if (commodity == null) - LogWriter.println(String.format("Can't find commodity from Gams demand, so ignoring: " + commodityName)); - else { - double subsGams = rec.getLevel(); - double discGams = modelStatus==0 ? 0 : varDisc.findRecord(commodityName).getLevel(); //modelStatus==0 is optimiser not run, i.e. too low GDP - GamsCommodityDemand demand = new GamsCommodityDemand(commodity, subsGams, discGams,inputData.getKcalPerT(commodity)); - LogWriter.println(demand.toString()); - demandMap.add(demand); - unhandledComms.remove(commodity); - } - } - - for (CommodityType commodity : unhandledComms) { - LogWriter.println(commodity + " not found in Gams output so adding with zero demand"); - demandMap.add(new GamsCommodityDemand(commodity, 0, 0, 0)); - } - - GAMSParameter parConsumpFact = outDB.getParameter("desiredConsumpFactor"); - double desiredConsumpFactor = parConsumpFact.getFirstRecord().getValue(); - LogWriter.println(String.format("desiredConsumpFactor=%.4f", desiredConsumpFactor)); - - return new GamsDemandOutput(status, demandMap, utility, desiredConsumpFactor); - } -} +package ac.ed.lurg.country.gams; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Vector; + +import com.gams.api.GAMSDatabase; +import com.gams.api.GAMSGlobals; +import com.gams.api.GAMSJob; +import com.gams.api.GAMSOptions; +import com.gams.api.GAMSParameter; +import com.gams.api.GAMSParameterRecord; +import com.gams.api.GAMSVariable; +import com.gams.api.GAMSVariableRecord; +import com.gams.api.GAMSWorkspace; +import com.gams.api.GAMSWorkspaceInfo; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.types.CommodityType; +import ac.ed.lurg.utils.LogWriter; + +public class GamsDemandOptimiser { + + private GamsDemandInput inputData; + + public GamsDemandOptimiser(GamsDemandInput inputData) { + this.inputData = inputData; + } + + public GamsDemandOutput getDemandPc() { + File workingDirectory = new File(ModelConfig.TEMP_DIR); + workingDirectory.mkdir(); + GAMSWorkspaceInfo wsInfo = new GAMSWorkspaceInfo(); + wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath()); + + GAMSWorkspace ws = new GAMSWorkspace(wsInfo); + GAMSOptions opt = ws.addOptions(); + GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.DEMAND_GAMS_MODEL); + + GAMSDatabase inDB = ws.addDatabase(); + setupPriceGdpDB(inDB); + ArrayList<GAMSDatabase> dbs = new ArrayList<GAMSDatabase>(); + dbs.add(inDB); + opt.defines("gdx_prices_and_gdp", inDB.getName()); + + if (ModelConfig.ADJUST_DIET_PREFS) { + LogWriter.println("Adusting diet params"); + GAMSDatabase originalParamDb = ws.addDatabaseFromGDX(new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath()); + GAMSDatabase adjustedParamDb = ws.addDatabase(originalParamDb); + adjustDietParams(adjustedParamDb); + dbs.add(adjustedParamDb); + opt.defines("gdx_parameters", adjustedParamDb.getName()); + } + else + opt.defines("gdx_parameters", new File(ModelConfig.DEMAND_PARAM_FILE).getAbsolutePath()); + + long startTime = System.currentTimeMillis(); + gamsJob.run(opt, dbs.toArray(new GAMSDatabase[dbs.size()])); + + LogWriter.println("Took " + (System.currentTimeMillis() - startTime) + " ms to run"); + return handleResults(gamsJob.OutDB()); + } + + private void adjustDietParams(GAMSDatabase adjustedParamDb) { + // get original parameters + GAMSParameter tauParam = adjustedParamDb.getParameter("tau"); + TauCalculationManager tauManager = TauCalculationManager.getInstance(); + + for (GAMSParameterRecord rec : tauParam) { + String key = rec.getKeys()[0]; + double initialTau = rec.getValue(); + + CommodityType commodity = CommodityType.getForGamsName(key); + double adjusted = tauManager.getFinalTau(inputData.getYear(), initialTau, commodity); + LogWriter.println(String.format("%14s: initialTau=%.6f, adjusted=%.6f", key, initialTau, adjusted)); + rec.setValue(adjusted); + } + } + + private void setupPriceGdpDB(GAMSDatabase inDB) { + GAMSParameter gdpPcP = inDB.addParameter("gdp_pc", 0); + double gdpPc = inputData.getGdpPc(); + LogWriter.println(String.format("gdp_pc = %.5f", gdpPc)); + setGamsParamValue(gdpPcP.addRecord(), gdpPc, 5); + GamsDemandOutput previousGamsDemands = inputData.getPreviousDemands(); + + GAMSParameter prevUP = inDB.addParameter("previousU", 0); + double previousUtility = previousGamsDemands == null ? 0 : previousGamsDemands.getUtility();//-16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc); + LogWriter.println(String.format("previousU = %.5f, estimated = %.5f", previousUtility, -16.3829624069452 + 4.3659621833299 * Math.log10(gdpPc))); + setGamsParamValue(prevUP.addRecord(), previousUtility, 5); + + GAMSParameter maxIncomePropFoodSpendP = inDB.addParameter("maxIncomePropFoodSpend", 0); + setGamsParamValue(maxIncomePropFoodSpendP.addRecord(), ModelConfig.MAX_INCOME_PROPORTION_FOOD_SPEND, 4); + + LogWriter.println("\nPrice"); + GAMSParameter priceP = inDB.addParameter("price", 1); + GAMSParameter prevSubsP = inDB.addParameter("previousSubs", 1); + GAMSParameter prevDiscP = inDB.addParameter("previousDisc", 1); + + for (Map.Entry<CommodityType, Double> entry : inputData.getPrices().entrySet()) { + CommodityType comm = entry.getKey(); + double priceComm = entry.getValue(); + doCommodity(inDB, priceP, prevSubsP, prevDiscP, gdpPc, previousGamsDemands, comm, priceComm, + comm==CommodityType.NONFOOD ? 5 : 0.05, comm==CommodityType.NONFOOD ? 0.5 : 0.05); + } + } + + private void doCommodity(GAMSDatabase inDB, GAMSParameter priceP, GAMSParameter prevSubsP, GAMSParameter prevDiscP, double gdpPc, + GamsDemandOutput previousGamsDemands, CommodityType comm, double price, double defaultSubs, double defaultDisc) { + + Vector<String> v = new Vector<String>(); + v.add(comm.getGamsName()); + double prevSubs = defaultSubs, prevDisc = defaultDisc; + + if (previousGamsDemands != null) { + GamsCommodityDemand demand = previousGamsDemands.getGamsDemands(comm); + prevSubs = demand.getSubsistence(); + prevDisc = demand.getDiscretionary(); + } + + LogWriter.println(String.format("%14s: price=%.4f, previousSubs=%.4f, previousDisc=%.4f", comm.getGamsName(), price, prevSubs, prevDisc)); + setGamsParamValue(priceP.addRecord(v), price, 4); + setGamsParamValue(prevSubsP.addRecord(v), prevSubs, 4); + setGamsParamValue(prevDiscP.addRecord(v), prevDisc, 4); + } + + private double setGamsParamValue(GAMSParameterRecord param, double val, int places) { + double dOut = Math.round(val * Math.pow(10,places)) / Math.pow(10,places); + param.setValue(dOut); + return dOut; + } + + private GamsDemandOutput handleResults(GAMSDatabase outDB) { + int modelStatus = (int) outDB.getParameter("ms").findRecord().getValue(); + String status = GAMSGlobals.ModelStat.lookup(modelStatus).toString(); + LogWriter.println(String.format("\nDemand %s: Modelstatus (%d) %s, Solvestatus %s", inputData.getCountry(), + modelStatus, status, GAMSGlobals.SolveStat.lookup((int) outDB.getParameter("ss").findRecord().getValue()) )); + + GAMSVariable varU = outDB.getVariable("u"); + double utility = varU.getFirstRecord().getLevel(); + LogWriter.println(String.format("u = %.4f", utility)); + + GAMSVariable varSubs = outDB.getVariable("SubsistenceC"); + GAMSVariable varDisc = outDB.getVariable("DiscretionaryC"); + + HashSet<GamsCommodityDemand> demandMap = new HashSet<GamsCommodityDemand>(); + Collection<CommodityType> unhandledComms = new ArrayList<CommodityType>(CommodityType.getAllFoodItems()); + + for (GAMSVariableRecord rec : varSubs) { + String commodityName = rec.getKeys()[0]; + CommodityType commodity = CommodityType.getForGamsName(commodityName, true); + + if (commodity == null) + LogWriter.println(String.format("Can't find commodity from Gams demand, so ignoring: " + commodityName)); + else { + double subsGams = rec.getLevel(); + double discGams = modelStatus==0 ? 0 : varDisc.findRecord(commodityName).getLevel(); //modelStatus==0 is optimiser not run, i.e. too low GDP + GamsCommodityDemand demand = new GamsCommodityDemand(commodity, subsGams, discGams,inputData.getKcalPerT(commodity)); + LogWriter.println(demand.toString()); + demandMap.add(demand); + unhandledComms.remove(commodity); + } + } + + for (CommodityType commodity : unhandledComms) { + LogWriter.println(commodity + " not found in Gams output so adding with zero demand"); + demandMap.add(new GamsCommodityDemand(commodity, 0, 0, 0)); + } + + GAMSParameter parConsumpFact = outDB.getParameter("desiredConsumpFactor"); + double desiredConsumpFactor = parConsumpFact.getFirstRecord().getValue(); + LogWriter.println(String.format("desiredConsumpFactor=%.4f", desiredConsumpFactor)); + + return new GamsDemandOutput(status, demandMap, utility, desiredConsumpFactor); + } +} diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index 9ee25cda7f762b253831a8cc01eb69939e185aaf..e95480a37db8d26d283a2e1885a18d089d208c7a 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -1,658 +1,644 @@ -package ac.ed.lurg.country.gams; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Vector; - -import com.gams.api.GAMSDatabase; -import com.gams.api.GAMSException; -import com.gams.api.GAMSGlobals; -import com.gams.api.GAMSGlobals.ModelStat; -import com.gams.api.GAMSJob; -import com.gams.api.GAMSOptions; -import com.gams.api.GAMSParameter; -import com.gams.api.GAMSParameterRecord; -import com.gams.api.GAMSSet; -import com.gams.api.GAMSVariable; -import com.gams.api.GAMSVariableRecord; -import com.gams.api.GAMSWorkspace; -import com.gams.api.GAMSWorkspaceInfo; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.carbon.CarbonFluxItem; -import ac.ed.lurg.country.CountryPrice; -import ac.ed.lurg.country.LandCoverChangeItem; -import ac.ed.lurg.country.TradeConstraint; -import ac.ed.lurg.forestry.WoodYieldData; -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.LccKey; -import ac.ed.lurg.landuse.WoodUsageData; -import ac.ed.lurg.types.CommodityType; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.WoodType; -import ac.ed.lurg.types.YieldType; -import ac.ed.lurg.utils.LazyHashMap; -import ac.ed.lurg.utils.LogWriter; -import ac.ed.lurg.yield.YieldResponsesItem; - -public class GamsLocationOptimiser { - - private static final boolean DEBUG = false; - - private GamsLocationInput inputData; - - public GamsLocationOptimiser(GamsLocationInput inputData) { - this.inputData = inputData; - } - - public GamsLocationOutput run() { - - File workingDirectory = new File(ModelConfig.TEMP_DIR); - workingDirectory.mkdir(); - - GAMSWorkspaceInfo wsInfo = new GAMSWorkspaceInfo(); - wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath()); - // wsInfo.setDebugLevel(DebugLevel.VERBOSE); - - GAMSWorkspace ws = new GAMSWorkspace(wsInfo); - GAMSDatabase inDB = ws.addDatabase(); - - setupInDB(inDB); - - GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.GAMS_MODEL); - GAMSOptions opt = ws.addOptions(); - - opt.setAllModelTypes("conopt"); - - opt.defines("gdxincname", inDB.getName()); - - 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()); - - } - - private void setupInDB(GAMSDatabase inDB) { - //if (DEBUG) LogWriter.println("\nLocation set"); - GAMSSet locationSet = inDB.addSet("location", 1); - for (Integer locId : inputData.getPreviousLandUse().keySet()) { - //if (DEBUG) LogWriter.println(" " + locId); - locationSet.addRecord(locId.toString()); - } - - if (DEBUG) LogWriter.println("\nPrevious crop and land areas"); - GAMSParameter prevCropP = inDB.addParameter("previousCropArea", 2); - GAMSParameter prevFertIP = inDB.addParameter("previousFertIntensity", 2); - GAMSParameter prevIrrigIP = inDB.addParameter("previousIrrigIntensity", 2); - GAMSParameter prevOtherIP = inDB.addParameter("previousOtherIntensity", 2); - - GAMSParameter previousRuminantFeedP = inDB.addParameter("previousRuminantFeed", 1); - GAMSParameter previousMonogastricFeedP = inDB.addParameter("previousMonogastricFeed", 1); - GAMSParameter previousImportAmountP = inDB.addParameter("previousImportAmount", 1); - GAMSParameter previousExportAmountP = inDB.addParameter("previousExportAmount", 1); - - GAMSParameter landP = inDB.addParameter("suitableLandArea", 1); - GAMSParameter seedAndWasteRateP = inDB.addParameter("seedAndWasteRate", 1); - - GAMSParameter prevLandCoverP = inDB.addParameter("previousLandCoverArea", 2); - GAMSParameter infrastructureCostP = inDB.addParameter("infrastructureCost", 1); - - double totalAgriLand = 0; - double totalSuitable = 0; - - for (Map.Entry<Integer, ? extends LandUseItem> entry : inputData.getPreviousLandUse().entrySet()) { - Integer locationId = entry.getKey(); - String locString = Integer.toString(locationId); - LandUseItem landUseItem = entry.getValue(); - - double suitableLand = landUseItem.getSuitableArea(); - totalSuitable += suitableLand; - if (DEBUG) LogWriter.println(String.format(" %d %15s,\t %.3f", locationId, "suitableLand", suitableLand)); - setGamsParamValueTruncate(landP.addRecord(locString), suitableLand, 5); - - for (CropType cropType : CropType.getNonMeatTypes()) { - Vector<String> v = new Vector<String>(); - v.add(cropType.getGamsName()); - v.add(locString); - - double area; - if (CropType.PASTURE == cropType) - area = landUseItem.getLandCoverArea(LandCoverType.PASTURE); - else - area = landUseItem.getCropArea(cropType); - - double prevFertI, prevIrrigI, prevOtherI; - Intensity intensity = landUseItem.getIntensity(cropType); - - if (intensity==null) { // could be first time through or this crop not previously grown in this location, so give it some default values - prevFertI = CropType.PASTURE.equals(cropType) ? 0.0 : 0.5; - prevIrrigI = CropType.PASTURE.equals(cropType) ? 0.0 : 0.5; - prevOtherI = CropType.PASTURE.equals(cropType) ? 0.1 : 0.5; - } - else { - prevOtherI = intensity.getOtherIntensity(); - if (prevOtherI == 0 & !CropType.PASTURE.equals(cropType)) { // this is needed or optimizer gets a bit confused if some areas are kept as cropland but are not productive, due to zero other intensity (i.e. set aside) - prevFertI = 0.5; - prevIrrigI = 0.5; - } - else { - prevFertI = intensity.getFertiliserIntensity(); - prevIrrigI = intensity.getIrrigationIntensity(); - } - } - - if (DEBUG) LogWriter.println(String.format(" %d %15s,\t %.2f,\t %.3f,\t %.3f,\t %.3f", locationId, cropType.getGamsName(), area, prevFertI, prevIrrigI, prevOtherI)); - - setGamsParamValue(prevCropP.addRecord(v), area, 3); - setGamsParamValue(prevFertIP.addRecord(v), prevFertI, 4); - setGamsParamValue(prevIrrigIP.addRecord(v), prevIrrigI, 4); - setGamsParamValue(prevOtherIP.addRecord(v), prevOtherI, 4); - - totalAgriLand += area; - - } - - // Previous land covers - for (LandCoverType lc : LandCoverType.getConvertibleTypes()) { - Vector<String> v = new Vector<String>(); - v.add(lc.getName()); - v.add(locString); - setGamsParamValueTruncate(prevLandCoverP.addRecord(v), landUseItem.getConvertibleLandCoverArea(lc), 6); - } - - // Infrastructure cost TODO - double infrastructureCost = landUseItem.getLandCoverFract(LandCoverType.NATURAL) * 1; - setGamsParamValue(infrastructureCostP.addRecord(locString), infrastructureCost, 5); - - } - - if (DEBUG) LogWriter.println(String.format(" Total agricultural %.1f,\t suitable %.1f", totalAgriLand, totalSuitable)); - - if (DEBUG) LogWriter.println("\nIrrigation data (cost, constraint)"); - GAMSParameter irrigCostP = inDB.addParameter("irrigCost", 1); - GAMSParameter irrigConstraintP = inDB.addParameter("irrigConstraint", 1); - Map<Integer, ? extends IrrigationItem> irrigationData = inputData.getIrrigationCosts(); - - double irrigCostMultiplier = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(),"IRRIG_COST_MULTIPLIER"); - - for (Entry<Integer, ? extends IrrigationItem> entry : irrigationData.entrySet()) { - Integer locationId = entry.getKey(); - IrrigationItem irrigCostItem = entry.getValue(); - double irrigCost = irrigCostItem.getIrrigCost()*irrigCostMultiplier; - double irrigConstraint = irrigCostItem.getIrrigConstraint(); - if (DEBUG) LogWriter.println(String.format(" %d \t %.5f,\t %.1f", locationId, irrigCost, irrigConstraint)); - setGamsParamValue(irrigCostP.addRecord(Integer.toString(locationId)), irrigCost, 5); - setGamsParamValue(irrigConstraintP.addRecord(Integer.toString(locationId)), irrigConstraint, 3); - } - - if (DEBUG) LogWriter.println("\nDemand: " + inputData.getCountryInput().getCountry() + " " + inputData.getTimestep().getYear()); - GamsCountryInput countryInput = inputData.getCountryInput(); - addCommodityMapParm(inDB.addParameter("demand", 1), countryInput.getProjectedDemand(), -1); - - GAMSParameter minCerealFracP = inDB.addParameter("minDemandPerCereal", 1); - GAMSParameter minOilcropsFracP = inDB.addParameter("minDemandPerOilcrop", 1); - if (DEBUG) LogWriter.println("\nMinDemandFractions"); - - for (Entry<CommodityType, Map<CropType, Double>> entry : countryInput.getMinDemandFractions().entrySet()) { - CommodityType comm = entry.getKey(); - for (Map.Entry<CropType, Double> entry2 : entry.getValue().entrySet()) { - CropType crop = entry2.getKey(); - double minCommFract = ModelConfig.LIMIT_DEMAND_FRACTION ? entry2.getValue() : 0.0; - GAMSParameter minCropsFracP = (comm == CommodityType.CEREALS ? minCerealFracP : minOilcropsFracP); - setGamsParamValueTruncate(minCropsFracP.addRecord(crop.getGamsName()), minCommFract, 4); - if (DEBUG) LogWriter.println(String.format(" %15s, %10s, %.4f", comm.getGamsName(), crop.getGamsName(), minCommFract)); - } - } - - if (DEBUG) LogWriter.println("\nYield (fert/irrig) None/None, Max/None, None/Max, Max/Max, Shock,\t [fert p],\t [irrig p],\t {max irrig}"); - GAMSParameter yNoneP = inDB.addParameter("yieldNone", 2); - GAMSParameter y_fert = inDB.addParameter("yieldFertOnly", 2); - GAMSParameter y_irrig = inDB.addParameter("yieldIrrigOnly", 2); - GAMSParameter y_both = inDB.addParameter("yieldBoth", 2); - GAMSParameter y_shock = inDB.addParameter("yieldShock", 2); - GAMSParameter fert_p = inDB.addParameter("fertParam", 2); - GAMSParameter irrig_p = inDB.addParameter("irrigParam", 2); - GAMSParameter irrigMaxP = inDB.addParameter("irrigMaxRate", 2); - - for (Entry<Integer, ? extends YieldResponsesItem> entry : inputData.getYields().entrySet()) { - Integer locationId = entry.getKey(); - String locString = Integer.toString(locationId); - YieldResponsesItem yresp = entry.getValue(); - IrrigationItem irrigationItem = irrigationData.get(locationId); - - for (CropType crop : CropType.getNonMeatTypes()) { - Vector<String> v = new Vector<String>(); - v.add(crop.getGamsName()); - v.add(locString); - - if (crop.equals(CropType.SETASIDE)) { - setGamsParamValue(irrigMaxP.addRecord(v), 1000, 3); // need to set this to any positive value to give an incentive not to irrigate setaside - continue; - } - - double maxIrrig = irrigationItem.getMaxIrrigAmount(crop); - - if (DEBUG) LogWriter.println(String.format("%d %15s,\t %.1f,\t %.1f, \t %.1f,\t %.1f,\t %.2f,\t\t [%.2f],\t [%.2f],\t {%.2f}", - locationId, crop.getGamsName(), - yresp.getExtrapolatedYield(YieldType.NO_FERT_NO_IRRIG, crop), - yresp.getExtrapolatedYield(YieldType.FERT_MAX_NO_IRRIG, crop), - yresp.getExtrapolatedYield(YieldType.NO_FERT_IRRIG_MAX, crop), - yresp.getExtrapolatedYield(YieldType.FERT_MAX_IRRIG_MAX, crop), - yresp.getShockRate(crop), yresp.getFertParam(crop), yresp.getIrrigParam(crop), maxIrrig)); - - setGamsParamValue(yNoneP.addRecord(v), yresp.getExtrapolatedYield(YieldType.NO_FERT_NO_IRRIG, crop), 4); - setGamsParamValue(y_fert.addRecord(v), yresp.getExtrapolatedYield(YieldType.FERT_MAX_NO_IRRIG, crop), 4); - setGamsParamValue(y_irrig.addRecord(v), yresp.getExtrapolatedYield(YieldType.NO_FERT_IRRIG_MAX, crop), 4); - setGamsParamValue(y_both.addRecord(v), yresp.getExtrapolatedYield(YieldType.FERT_MAX_IRRIG_MAX, crop), 4); - setGamsParamValue(y_shock.addRecord(v), yresp.getShockRate(crop), 4); - setGamsParamValue(fert_p.addRecord(v), yresp.getFertParam(crop), 4); - setGamsParamValue(irrig_p.addRecord(v), yresp.getIrrigParam(crop), 4); - setGamsParamValue(irrigMaxP.addRecord(v), maxIrrig, 3); - } - } - - if (DEBUG) LogWriter.println("\nCrop, subsidy rate"); - GAMSParameter subsideRateP = inDB.addParameter("subsidyRate", 1); - for (CropType crop : CropType.getNonMeatTypes()) { - double subsidyRate = countryInput.getSubsidyRates().get(crop); - if (DEBUG) LogWriter.println(String.format("%15s,\t %.4f", crop.getGamsName(), subsidyRate)); - setGamsParamValue(subsideRateP.addRecord(crop.getGamsName()), subsidyRate, 4); - } - - if (DEBUG) LogWriter.println("\nImport-export, min trade/prod, max trade/prod, global import price, global export price, imports, exports, ruminantFeed, monogastricFeed, seedAndWasteRate"); - - GAMSParameter minTradeP = null; - GAMSParameter maxTradeP = null; - minTradeP = inDB.addParameter("minNetImport", 1); - maxTradeP = inDB.addParameter("maxNetImport", 1); - - GAMSParameter importPriceP = inDB.addParameter("importPrices", 1); - GAMSParameter exportPriceP = inDB.addParameter("exportPrices", 1); - - for (CropType crop : CropType.getImportedTypes()) { - - TradeConstraint iec = countryInput.getTradeConstraints().get(crop); - CountryPrice gp = countryInput.getCountryPrices().get(crop); - double minTrade = iec.getMinConstraint(); - double maxTrade = iec.getMaxConstraint(); - double importPrice = gp.getImportPrice(); - double exportPrice = gp.getExportPrice(); - - CropUsageData cu = countryInput.getPreviousCropUsageData().get(crop); - double netImports = cu.getNetImportsExpected(); - double imports = netImports>0 ? netImports : 0; - double exports = netImports<0 ? -netImports : 0; - - double ruminantFeed = cu.getRuminantFeed(); - double monogastricFeed = cu.getMonogastricFeed(); - double seedAndWasteRate = crop.getSeedAndWasteRate(); - - if (DEBUG) LogWriter.println(String.format(" %15s, \t %5.1f, \t %5.1f, \t %5.3f, \t %5.3f, \t %5.1f, \t %5.1f, \t %5.1f, \t %5.1f, \t %5.3f", - crop.getGamsName(), minTrade, maxTrade, importPrice, exportPrice, imports, exports, ruminantFeed, monogastricFeed, seedAndWasteRate)); - - setGamsParamValue(minTradeP.addRecord(crop.getGamsName()), minTrade, 3); - setGamsParamValue(maxTradeP.addRecord(crop.getGamsName()), maxTrade, 3); - setGamsParamValue(importPriceP.addRecord(crop.getGamsName()), importPrice, 3); - setGamsParamValue(exportPriceP.addRecord(crop.getGamsName()), exportPrice, 3); - setGamsParamValue(previousImportAmountP.addRecord(crop.getGamsName()), imports, 3); - setGamsParamValue(previousExportAmountP.addRecord(crop.getGamsName()), exports, 3); - setGamsParamValue(previousRuminantFeedP.addRecord(crop.getGamsName()), ruminantFeed, 3); - setGamsParamValue(previousMonogastricFeedP.addRecord(crop.getGamsName()), monogastricFeed, 3); - setGamsParamValue(seedAndWasteRateP.addRecord(crop.getGamsName()), seedAndWasteRate, 3); - } - - - //below in case running without shocks to use original values in model config - - double meatEff = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(), "MEAT_EFFICIENCY"); - double fertCost = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(), "FERTILISER_COST_PER_T"); - double otherIntCost = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(), "OTHER_INTENSITY_COST"); - - LogWriter.print("\n"); - addScalar(inDB, "meatEfficency", meatEff, 3); - addScalar(inDB, "fertiliserUnitCost", fertCost, 3); - addScalar(inDB, "otherICost",otherIntCost, 3); - addScalar(inDB, "otherIParam", ModelConfig.OTHER_INTENSITY_PARAM, 3); - addScalar(inDB, "unhandledCropRate", ModelConfig.UNHANDLED_CROP_RATE, 3); - addScalar(inDB, "setAsideRate", ModelConfig.SETASIDE_RATE, 5); - addScalar(inDB, "domesticPriceMarkup", ModelConfig.DOMESTIC_PRICE_MARKUP, 3); - double maxExpansion = 1.0; - if (!ModelConfig.IS_CALIBRATION_RUN && countryInput.getCountry().getName().equals("China")) { - maxExpansion = ModelConfig.MAX_CHINA_LAND_EXPANSION_RATE; - } - addScalar(inDB, "maxLandExpansionRate", maxExpansion, 3); - - addScalar(inDB, "carbonPrice", countryInput.getCarbonPrice().getExportPrice(), 5); - addScalar(inDB, "woodExportPrice", countryInput.getWoodPrice().getExportPrice(), 5); - addScalar(inDB, "woodImportPrice", countryInput.getWoodPrice().getImportPrice(), 5); - - // Not simulating production of different wood products so need to aggregate - double woodMinNetImport = 0; - double woodMaxNetImport = 0; - Map<WoodType, TradeConstraint> woodTradeConstraints = countryInput.getWoodTradeConstraints(); - for (TradeConstraint tc: woodTradeConstraints.values()) { - woodMinNetImport += tc.getMinConstraint(); - woodMaxNetImport += tc.getMaxConstraint(); - } - - addScalar(inDB, "woodMaxNetImport", woodMaxNetImport, 5); - addScalar(inDB, "woodMinNetImport", woodMinNetImport, 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); - GAMSParameter cFluxRateNeeP = inDB.addParameter("carbonFluxRateNEE", 2); - - for (Entry<Integer, ? extends CarbonFluxItem> entry : inputData.getCarbonFluxes().entrySet()) { - Integer locationId = entry.getKey(); - String locString = Integer.toString(locationId); - CarbonFluxItem cFlux = entry.getValue(); - - for (Map.Entry<LccKey, Double> cfEntry : cFlux.getConversionCarbonFluxMap().entrySet()) { - LccKey key = cfEntry.getKey(); - Vector<String> v = new Vector<String>(); - v.add(key.getFromLc().getName()); - v.add(key.getToLc().getName()); - v.add(locString); - setGamsParamValue(cFluxRateP.addRecord(v), cfEntry.getValue(), 2); - } - - for (Map.Entry<LandCoverType, Double> cfEntry : cFlux.getEcosystemCarbonFluxMap().entrySet()) { - Vector<String> v = new Vector<String>(); - v.add(cfEntry.getKey().getName()); - v.add(locString); - setGamsParamValue(cFluxRateNeeP.addRecord(v), cfEntry.getValue(), 3); - } - } - - // Yield from timber forest rotation - Map<Integer, ? extends WoodYieldData> woodYieldData = inputData.getWoodYields(); - - GAMSParameter woodYieldRotaP = inDB.addParameter("woodYieldRota", 1); - GAMSParameter woodYieldLuc = inDB.addParameter("woodYieldLUC", 3); - - for (Entry<Integer, ? extends WoodYieldData> entry : woodYieldData.entrySet()) { - Integer locationId = entry.getKey(); - String locString = Integer.toString(locationId); - WoodYieldData wYield = entry.getValue(); - - Vector<String> v = new Vector<String>(); - v.add(locString); - setGamsParamValue(woodYieldRotaP.addRecord(v), wYield.getYieldRota(), 5); - - for (LandCoverType toLc : LandCoverType.getAgriculturalTypes()) { - Vector<String> w = new Vector<String>(); - w.add(LandCoverType.NATURAL.getName()); - w.add(toLc.getName()); - w.add(locString); - setGamsParamValue(woodYieldLuc.addRecord(w), wYield.getYieldLuc(LandCoverType.NATURAL, toLc), 5); - } - - } - - // Land cover conversion cost - GAMSParameter conversionCostP = inDB.addParameter("conversionCost", 2); - Map<LccKey, Double> conversionCosts = inputData.getConversionCosts(); - - for (Map.Entry<LccKey, Double> entry : conversionCosts.entrySet()) { - LccKey key = entry.getKey(); - String fromName = key.getFromLc().getName(); - String toName = key.getToLc().getName(); - double cost = entry.getValue(); - Vector<String> v = new Vector<String>(); - v.add(fromName); - v.add(toName); - setGamsParamValue(conversionCostP.addRecord(v), cost, 5); - } - - GAMSParameter forestRotationP = inDB.addParameter("forestRotationPeriod", 1); - for (Map.Entry<Integer, Double> entry : inputData.getRotationPeriods().entrySet()) { - int locationId = entry.getKey(); - double rotPeriod = entry.getValue(); - String locString = Integer.toString(locationId); - setGamsParamValue(forestRotationP.addRecord(locString), rotPeriod, 0); - } - - addScalar(inDB, "forestEstablishmentCost", ModelConfig.FOREST_ESTABLISHMENT_COST, 5); - double managedWoodHarvest = countryInput.getWoodHarvestFromRotation(); - addScalar(inDB, "woodFromRotation", managedWoodHarvest, 5); - addScalar(inDB, "vegClearingCostRate", ModelConfig.VEGETATION_CLEARING_COST, 5); - } - - private void addScalar(GAMSDatabase gamsDb, String recordName, double val, int places) { - GAMSParameter param = gamsDb.addParameter(recordName, 0); - double dOut = setGamsParamValue(param.addRecord(), val, places); - if (DEBUG) LogWriter.println(recordName + ": " + dOut); - } - - private double setGamsParamValue(GAMSParameterRecord param, double val, int places) { - double dOut = places >= 0 ? Math.round(val * Math.pow(10,places)) / Math.pow(10,places) : val; - param.setValue(dOut); - return dOut; - } - - private double setGamsParamValueTruncate(GAMSParameterRecord param, double val, int places) { - double dOut = ((int)(val * Math.pow(10,places))) / Math.pow(10,places); - param.setValue(dOut); - return dOut; - } - - @SuppressWarnings("serial") - private GamsLocationOutput handleResults(GAMSDatabase outDB) { - int modelStatusInt = (int) outDB.getParameter("ms").findRecord().getValue(); - ModelStat modelStatus = GAMSGlobals.ModelStat.lookup(modelStatusInt); - String contextString = String.format("%s %s: Modelstatus %s, Solvestatus %s", - inputData.getCountryInput().getCountry(), - inputData.getTimestep().getYear(), - modelStatus.toString(), - GAMSGlobals.SolveStat.lookup((int) outDB.getParameter("ss").findRecord().getValue())); - LogWriter.println("\n" + contextString); - - if (modelStatus != ModelStat.OPTIMAL_LOCAL) { - LogWriter.printlnError("Critical!!! Land use incorrectly solved. " + contextString); - } - - GAMSVariable varAreas = outDB.getVariable("cropArea"); - GAMSVariable varFertIntensities = outDB.getVariable("fertI"); - GAMSVariable varIrrigIntensities = outDB.getVariable("irrigI"); - GAMSVariable varOtherIntensities = outDB.getVariable("otherIntensity"); - GAMSVariable varRuminantFeed = outDB.getVariable("ruminantFeed"); - GAMSVariable varMonogastricFeed = outDB.getVariable("monogastricFeed"); - GAMSParameter parmNetImports = outDB.getParameter("netImportAmount"); - GAMSParameter parmNetImportCost = outDB.getParameter("netImportCost"); - GAMSVariable varYields = outDB.getVariable("yield"); - GAMSVariable varUnitEnergies = outDB.getVariable("unitCost"); - GAMSParameter parmProd = outDB.getParameter("totalProd"); - GAMSParameter parmProdCost = outDB.getParameter("totalProdCost"); - GAMSParameter parmCroplandArea = outDB.getParameter("totalCropland"); - GAMSParameter parmTotalArea = outDB.getParameter("totalArea"); - GAMSParameter parmProdShock = outDB.getParameter("productionShock"); - - double totalCropArea = 0; - double totalPastureArea = 0; - double area, fertIntensity, irrigIntensity, otherIntensity = Double.NaN, ruminantFeed, monogastricFeed, netImport, netImportCost, yield, unitCost, prod, prodCost; - - final LazyHashMap<Integer, LandUseItem> landUses = new LazyHashMap<Integer, LandUseItem>() { - protected LandUseItem createValue() { return new LandUseItem(); } - }; - - Map<Integer, ? extends IrrigationItem> allIrrigationRefData = inputData.getIrrigationCosts(); - - Map<CropType, CropUsageData> cropUsageData = new HashMap<CropType, CropUsageData>(); - - for (GAMSVariableRecord rec : varAreas) { - String itemName = rec.getKeys()[0]; - String locationName = rec.getKeys()[1]; - area = rec.getLevel(); - fertIntensity = varFertIntensities.findRecord(itemName, locationName).getLevel(); - irrigIntensity = varIrrigIntensities.findRecord(itemName, locationName).getLevel(); - otherIntensity = varOtherIntensities.findRecord(itemName, locationName).getLevel(); - yield = varYields.findRecord(itemName, locationName).getLevel(); - unitCost = varUnitEnergies.findRecord(itemName, locationName).getLevel(); - - int locId = Integer.parseInt(locationName); - CropType cropType = CropType.getForGamsName(itemName); - - if (!cropUsageData.containsKey(cropType)) { // then we must not have seen this crop type before, so need to do all non location specific stuff - ruminantFeed = varRuminantFeed.findRecord(itemName).getLevel(); - monogastricFeed = varMonogastricFeed.findRecord(itemName).getLevel(); - netImport = cropType.isImportedCrop() ? getParmValue(parmNetImports, itemName) : 0; - netImportCost = cropType.isImportedCrop() ? getParmValue(parmNetImportCost, itemName) : 0; - prod = getParmValue(parmProd, itemName); - prodCost = getParmValue(parmProdCost, itemName); - double totalArea = getParmValue(parmTotalArea, itemName); - double prodShock = getParmValue(parmProdShock, itemName); - - cropUsageData.put(cropType, new CropUsageData(ruminantFeed, monogastricFeed, netImport, netImportCost, prod, prodCost, totalArea, prodShock)); - if (DEBUG) LogWriter.println(String.format("\n%s:\tarea= %.1f,\tmonogastricFeed= %.1f,\truminantFeed= %.1f,\tnetImports= %.3f,\tnetImportCost= %.3f,\tprod= %.3f, \tprodCost= %.3f,\tprodCostRate= %.3f,\tprodShock= %.3f", itemName, totalArea, monogastricFeed, ruminantFeed, netImport, netImportCost, prod, prodCost, prodCost/prod, prodShock)); - - } - - LandUseItem landUseItem = landUses.lazyGet(locId); // returns landUseItem for location. If does not exist, creates new one - - if (area > 0) { - - if (DEBUG) LogWriter.println(String.format("\t location %s, %s:\tarea= %.1f,\tfert= %.3f,\tirrg= %.3f,\tintensity= %.3f", locationName, itemName, area, fertIntensity, irrigIntensity, otherIntensity)); - IrrigationItem irrigRefData = allIrrigationRefData.get(locId); - landUseItem.setIntensity(cropType, new Intensity(fertIntensity, irrigIntensity, otherIntensity, yield, unitCost, irrigRefData.getMaxIrrigAmount(cropType))); - } - - - double croplandArea = getParmValue(parmCroplandArea, locationName); - - landUseItem.setCropFraction(cropType, croplandArea > 0 ? area/croplandArea : 0); // TODO gives wrong pasture numbers - - } - - for (CropType meatTypes : CropType.getMeatTypes()) { - netImport = getParmValue(parmNetImports, meatTypes.getGamsName()); - netImportCost= getParmValue(parmNetImportCost, meatTypes.getGamsName()); - prod = getParmValue(parmProd, meatTypes.getGamsName()); - prodCost = getParmValue(parmProdCost, meatTypes.getGamsName()); - - cropUsageData.put(meatTypes, new CropUsageData(0.0, 0.0, netImport, netImportCost, prod, prodCost, Double.NaN, 0)); - if (DEBUG) LogWriter.println(String.format("\n%s:\t\t\t\t\tnetImports= %.3f,\tnetImportCost= %.3f,\tprod= %.3f,\tprodCost= %.3f", meatTypes.getGamsName(), netImport, netImportCost, prod, prodCost)); - } - LogWriter.println(String.format("\n%s %s: Total area= %.1f (crop=%.1f, pasture %.1f)", - inputData.getCountryInput().getCountry(), inputData.getTimestep().getYear(), - totalCropArea+totalPastureArea, totalCropArea, totalPastureArea)); - - // Land cover change - GAMSVariable varLandCoverChange = outDB.getVariable("landCoverChange"); - Map<Integer, ArrayList<LandCoverChangeItem>> landCoverChanges = new HashMap<Integer, ArrayList<LandCoverChangeItem>>(); - - for (GAMSVariableRecord rec : varLandCoverChange) { - String fromLcStr = rec.getKeys()[0]; - String toLcStr = rec.getKeys()[1]; - String locationName = rec.getKeys()[2]; - int locId = Integer.parseInt(locationName); - double change = rec.getLevel(); - - // Skip if no change or no conversion - if (change == 0 || fromLcStr.equals(toLcStr)) - continue; - - LandCoverType fromLc = LandCoverType.getForName(fromLcStr); - LandCoverType toLc = LandCoverType.getForName(toLcStr); - - ArrayList<LandCoverChangeItem> changesList = landCoverChanges.computeIfAbsent(locId, k -> new ArrayList<LandCoverChangeItem>()); - changesList.add(new LandCoverChangeItem(fromLc, toLc, change)); - } - - // Carbon flux - double netCarbonFlux = outDB.getParameter("netCarbonFlux").getFirstRecord().getValue(); - - // Timber harvest - double totalWoodHarvest = outDB.getParameter("totalWoodHarvest").getFirstRecord().getValue(); - double netWoodImport = outDB.getParameter("netWoodImport").getFirstRecord().getValue(); - - // Need to disaggregate wood harvest and import by type. This is just for future proofing. Eventually, could have separate harvest for each wood type. - Map<WoodType, WoodUsageData> newWoodUsageMap = new HashMap<WoodType, WoodUsageData>(); - Map<WoodType, WoodUsageData> previousWoodUsageMap = inputData.getCountryInput().getPreviousWoodUsageData(); - double previousNetImportSum = previousWoodUsageMap.values().stream().mapToDouble(o -> o.getNetImport()).sum(); - double netWoodImportChange = netWoodImport - previousNetImportSum; - double previousWoodHarvestSum = previousWoodUsageMap.values().stream().mapToDouble(o -> o.getHarvest()).sum(); - int numWoodTypes = WoodType.values().length; - for (WoodType wt : WoodType.values()) { - double previousNetImport = previousWoodUsageMap.get(wt).getNetImport(); - double newNetImport = previousNetImport + netWoodImportChange / numWoodTypes; - double previousHarvest = previousWoodUsageMap.get(wt).getHarvest(); - // assuming equal split if no previous harvest - double newHarvest = (previousWoodHarvestSum > 0) ? totalWoodHarvest * (previousHarvest / previousWoodHarvestSum) : totalWoodHarvest / numWoodTypes; - newWoodUsageMap.put(wt, new WoodUsageData(newHarvest, newNetImport)); - } - - Map<Integer, Double> naturalForestCleared = new HashMap<Integer, Double>(); - - GamsLocationOutput results = new GamsLocationOutput(modelStatus, landUses, cropUsageData, landCoverChanges, netCarbonFlux, newWoodUsageMap, naturalForestCleared); - return results; - } - - /* - private double getPrevUnmanagedForestProp(Integer locId) { - double prevOtherNaturalArea = inputData.getPreviousLandUse().get(locId).getUnprotectedLandCoverArea(LandCoverType.OTHER_NATURAL); - double prevUnmanagedForestArea = inputData.getPreviousLandUse().get(locId).getUnprotectedLandCoverArea(LandCoverType.UNMANAGED_FOREST); - double prevUnmanagedForestProp = (prevOtherNaturalArea + prevUnmanagedForestArea) != 0 ? prevUnmanagedForestArea / (prevUnmanagedForestArea + prevOtherNaturalArea) : 0; - return prevUnmanagedForestProp; - } - */ - - private double getParmValue(GAMSParameter aParm, String itemName) { - try { - GAMSParameterRecord record = aParm.findRecord(itemName); - double d = record.getValue(); - return d; - } - catch (GAMSException gamsEx) { - //LogWriter.println("GAMSException thrown for " + itemName); - return 0; - } - } - - private void addCommodityMapParm(GAMSParameter parm, Map<CommodityType, Double> itemMap, int places) { - for (Map.Entry<CommodityType, Double> entry : itemMap.entrySet()) { - double d = entry.getValue(); - if (DEBUG) LogWriter.println(String.format(" %15s,\t %.1f", entry.getKey().getGamsName(), d)); - if (!Double.isNaN(d)) - setGamsParamValue(parm.addRecord(entry.getKey().getGamsName()), d, places); - } - } - - - 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); - } - } - try { - directoryToDelete.delete(); - } catch(Exception e) { - LogWriter.print(e); - } - } -} +package ac.ed.lurg.country.gams; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Vector; + +import com.gams.api.GAMSDatabase; +import com.gams.api.GAMSException; +import com.gams.api.GAMSGlobals; +import com.gams.api.GAMSGlobals.ModelStat; +import com.gams.api.GAMSJob; +import com.gams.api.GAMSOptions; +import com.gams.api.GAMSParameter; +import com.gams.api.GAMSParameterRecord; +import com.gams.api.GAMSSet; +import com.gams.api.GAMSVariable; +import com.gams.api.GAMSVariableRecord; +import com.gams.api.GAMSWorkspace; +import com.gams.api.GAMSWorkspaceInfo; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.carbon.CarbonFluxItem; +import ac.ed.lurg.country.CountryPrice; +import ac.ed.lurg.country.LandCoverChangeItem; +import ac.ed.lurg.country.TradeConstraint; +import ac.ed.lurg.forestry.WoodYieldData; +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.LccKey; +import ac.ed.lurg.landuse.WoodUsageData; +import ac.ed.lurg.types.CommodityType; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.ed.lurg.types.WoodType; +import ac.ed.lurg.types.YieldType; +import ac.ed.lurg.utils.LazyHashMap; +import ac.ed.lurg.utils.LogWriter; +import ac.ed.lurg.yield.YieldResponsesItem; + +public class GamsLocationOptimiser { + + private static final boolean DEBUG = false; + + private GamsLocationInput inputData; + + public GamsLocationOptimiser(GamsLocationInput inputData) { + this.inputData = inputData; + } + + public GamsLocationOutput run() { + + File workingDirectory = new File(ModelConfig.TEMP_DIR); + workingDirectory.mkdir(); + + GAMSWorkspaceInfo wsInfo = new GAMSWorkspaceInfo(); + wsInfo.setWorkingDirectory(workingDirectory.getAbsolutePath()); + // wsInfo.setDebugLevel(DebugLevel.VERBOSE); + + GAMSWorkspace ws = new GAMSWorkspace(wsInfo); + GAMSDatabase inDB = ws.addDatabase(); + + setupInDB(inDB); + + GAMSJob gamsJob = ws.addJobFromFile(ModelConfig.GAMS_MODEL); + GAMSOptions opt = ws.addOptions(); + + opt.setAllModelTypes("conopt"); + + opt.defines("gdxincname", inDB.getName()); + + 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()); + + } + + private void setupInDB(GAMSDatabase inDB) { + //if (DEBUG) LogWriter.println("\nLocation set"); + GAMSSet locationSet = inDB.addSet("location", 1); + for (Integer locId : inputData.getPreviousLandUse().keySet()) { + //if (DEBUG) LogWriter.println(" " + locId); + locationSet.addRecord(locId.toString()); + } + + if (DEBUG) LogWriter.println("\nPrevious crop and land areas"); + GAMSParameter prevCropP = inDB.addParameter("previousCropArea", 2); + GAMSParameter prevFertIP = inDB.addParameter("previousFertIntensity", 2); + GAMSParameter prevIrrigIP = inDB.addParameter("previousIrrigIntensity", 2); + GAMSParameter prevOtherIP = inDB.addParameter("previousOtherIntensity", 2); + + GAMSParameter previousRuminantFeedP = inDB.addParameter("previousRuminantFeed", 1); + GAMSParameter previousMonogastricFeedP = inDB.addParameter("previousMonogastricFeed", 1); + GAMSParameter previousImportAmountP = inDB.addParameter("previousImportAmount", 1); + GAMSParameter previousExportAmountP = inDB.addParameter("previousExportAmount", 1); + + GAMSParameter landP = inDB.addParameter("suitableLandArea", 1); + GAMSParameter seedAndWasteRateP = inDB.addParameter("seedAndWasteRate", 1); + + GAMSParameter prevLandCoverP = inDB.addParameter("previousLandCoverArea", 2); + + double totalAgriLand = 0; + double totalSuitable = 0; + + for (Map.Entry<Integer, ? extends LandUseItem> entry : inputData.getPreviousLandUse().entrySet()) { + Integer locationId = entry.getKey(); + String locString = Integer.toString(locationId); + LandUseItem landUseItem = entry.getValue(); + + double suitableLand = landUseItem.getSuitableArea(); + totalSuitable += suitableLand; + if (DEBUG) LogWriter.println(String.format(" %d %15s,\t %.3f", locationId, "suitableLand", suitableLand)); + setGamsParamValueTruncate(landP.addRecord(locString), suitableLand, 5); + + for (CropType cropType : CropType.getNonMeatTypes()) { + Vector<String> v = new Vector<String>(); + v.add(cropType.getGamsName()); + v.add(locString); + + double area; + if (CropType.PASTURE == cropType) + area = landUseItem.getLandCoverArea(LandCoverType.PASTURE); + else + area = landUseItem.getCropArea(cropType); + + double prevFertI, prevIrrigI, prevOtherI; + Intensity intensity = landUseItem.getIntensity(cropType); + + if (intensity==null) { // could be first time through or this crop not previously grown in this location, so give it some default values + prevFertI = CropType.PASTURE.equals(cropType) ? 0.0 : 0.5; + prevIrrigI = CropType.PASTURE.equals(cropType) ? 0.0 : 0.5; + prevOtherI = CropType.PASTURE.equals(cropType) ? 0.1 : 0.5; + } + else { + prevOtherI = intensity.getOtherIntensity(); + if (prevOtherI == 0 & !CropType.PASTURE.equals(cropType)) { // this is needed or optimizer gets a bit confused if some areas are kept as cropland but are not productive, due to zero other intensity (i.e. set aside) + prevFertI = 0.5; + prevIrrigI = 0.5; + } + else { + prevFertI = intensity.getFertiliserIntensity(); + prevIrrigI = intensity.getIrrigationIntensity(); + } + } + + if (DEBUG) LogWriter.println(String.format(" %d %15s,\t %.2f,\t %.3f,\t %.3f,\t %.3f", locationId, cropType.getGamsName(), area, prevFertI, prevIrrigI, prevOtherI)); + + setGamsParamValue(prevCropP.addRecord(v), area, 3); + setGamsParamValue(prevFertIP.addRecord(v), prevFertI, 4); + setGamsParamValue(prevIrrigIP.addRecord(v), prevIrrigI, 4); + setGamsParamValue(prevOtherIP.addRecord(v), prevOtherI, 4); + + totalAgriLand += area; + + } + + // Previous land covers + for (LandCoverType lc : LandCoverType.getConvertibleTypes()) { + Vector<String> v = new Vector<String>(); + v.add(lc.getName()); + v.add(locString); + double area = landUseItem.getLandCoverArea(lc, LandProtectionType.CONVERTIBLE); + setGamsParamValueTruncate(prevLandCoverP.addRecord(v), area, 6); + } + } + + if (DEBUG) LogWriter.println(String.format(" Total agricultural %.1f,\t suitable %.1f", totalAgriLand, totalSuitable)); + + if (DEBUG) LogWriter.println("\nIrrigation data (cost, constraint)"); + GAMSParameter irrigCostP = inDB.addParameter("irrigCost", 1); + GAMSParameter irrigConstraintP = inDB.addParameter("irrigConstraint", 1); + Map<Integer, ? extends IrrigationItem> irrigationData = inputData.getIrrigationCosts(); + + double irrigCostMultiplier = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(),"IRRIG_COST_MULTIPLIER"); + + for (Entry<Integer, ? extends IrrigationItem> entry : irrigationData.entrySet()) { + Integer locationId = entry.getKey(); + IrrigationItem irrigCostItem = entry.getValue(); + double irrigCost = irrigCostItem.getIrrigCost()*irrigCostMultiplier; + double irrigConstraint = irrigCostItem.getIrrigConstraint(); + if (DEBUG) LogWriter.println(String.format(" %d \t %.5f,\t %.1f", locationId, irrigCost, irrigConstraint)); + setGamsParamValue(irrigCostP.addRecord(Integer.toString(locationId)), irrigCost, 5); + setGamsParamValue(irrigConstraintP.addRecord(Integer.toString(locationId)), irrigConstraint, 3); + } + + if (DEBUG) LogWriter.println("\nDemand: " + inputData.getCountryInput().getCountry() + " " + inputData.getTimestep().getYear()); + GamsCountryInput countryInput = inputData.getCountryInput(); + addCommodityMapParm(inDB.addParameter("demand", 1), countryInput.getProjectedDemand(), -1); + + GAMSParameter minCerealFracP = inDB.addParameter("minDemandPerCereal", 1); + GAMSParameter minOilcropsFracP = inDB.addParameter("minDemandPerOilcrop", 1); + if (DEBUG) LogWriter.println("\nMinDemandFractions"); + + for (Entry<CommodityType, Map<CropType, Double>> entry : countryInput.getMinDemandFractions().entrySet()) { + CommodityType comm = entry.getKey(); + for (Map.Entry<CropType, Double> entry2 : entry.getValue().entrySet()) { + CropType crop = entry2.getKey(); + double minCommFract = ModelConfig.LIMIT_DEMAND_FRACTION ? entry2.getValue() : 0.0; + GAMSParameter minCropsFracP = (comm == CommodityType.CEREALS ? minCerealFracP : minOilcropsFracP); + setGamsParamValueTruncate(minCropsFracP.addRecord(crop.getGamsName()), minCommFract, 4); + if (DEBUG) LogWriter.println(String.format(" %15s, %10s, %.4f", comm.getGamsName(), crop.getGamsName(), minCommFract)); + } + } + + if (DEBUG) LogWriter.println("\nYield (fert/irrig) None/None, Max/None, None/Max, Max/Max, Shock,\t [fert p],\t [irrig p],\t {max irrig}"); + GAMSParameter yNoneP = inDB.addParameter("yieldNone", 2); + GAMSParameter y_fert = inDB.addParameter("yieldFertOnly", 2); + GAMSParameter y_irrig = inDB.addParameter("yieldIrrigOnly", 2); + GAMSParameter y_both = inDB.addParameter("yieldBoth", 2); + GAMSParameter y_shock = inDB.addParameter("yieldShock", 2); + GAMSParameter fert_p = inDB.addParameter("fertParam", 2); + GAMSParameter irrig_p = inDB.addParameter("irrigParam", 2); + GAMSParameter irrigMaxP = inDB.addParameter("irrigMaxRate", 2); + + for (Entry<Integer, ? extends YieldResponsesItem> entry : inputData.getYields().entrySet()) { + Integer locationId = entry.getKey(); + String locString = Integer.toString(locationId); + YieldResponsesItem yresp = entry.getValue(); + IrrigationItem irrigationItem = irrigationData.get(locationId); + + for (CropType crop : CropType.getNonMeatTypes()) { + Vector<String> v = new Vector<String>(); + v.add(crop.getGamsName()); + v.add(locString); + + if (crop.equals(CropType.SETASIDE)) { + setGamsParamValue(irrigMaxP.addRecord(v), 1000, 3); // need to set this to any positive value to give an incentive not to irrigate setaside + continue; + } + + double maxIrrig = irrigationItem.getMaxIrrigAmount(crop); + + if (DEBUG) LogWriter.println(String.format("%d %15s,\t %.1f,\t %.1f, \t %.1f,\t %.1f,\t %.2f,\t\t [%.2f],\t [%.2f],\t {%.2f}", + locationId, crop.getGamsName(), + yresp.getExtrapolatedYield(YieldType.NO_FERT_NO_IRRIG, crop), + yresp.getExtrapolatedYield(YieldType.FERT_MAX_NO_IRRIG, crop), + yresp.getExtrapolatedYield(YieldType.NO_FERT_IRRIG_MAX, crop), + yresp.getExtrapolatedYield(YieldType.FERT_MAX_IRRIG_MAX, crop), + yresp.getShockRate(crop), yresp.getFertParam(crop), yresp.getIrrigParam(crop), maxIrrig)); + + setGamsParamValue(yNoneP.addRecord(v), yresp.getExtrapolatedYield(YieldType.NO_FERT_NO_IRRIG, crop), 4); + setGamsParamValue(y_fert.addRecord(v), yresp.getExtrapolatedYield(YieldType.FERT_MAX_NO_IRRIG, crop), 4); + setGamsParamValue(y_irrig.addRecord(v), yresp.getExtrapolatedYield(YieldType.NO_FERT_IRRIG_MAX, crop), 4); + setGamsParamValue(y_both.addRecord(v), yresp.getExtrapolatedYield(YieldType.FERT_MAX_IRRIG_MAX, crop), 4); + setGamsParamValue(y_shock.addRecord(v), yresp.getShockRate(crop), 4); + setGamsParamValue(fert_p.addRecord(v), yresp.getFertParam(crop), 4); + setGamsParamValue(irrig_p.addRecord(v), yresp.getIrrigParam(crop), 4); + setGamsParamValue(irrigMaxP.addRecord(v), maxIrrig, 3); + } + } + + if (DEBUG) LogWriter.println("\nCrop, subsidy rate"); + GAMSParameter subsideRateP = inDB.addParameter("subsidyRate", 1); + for (CropType crop : CropType.getNonMeatTypes()) { + double subsidyRate = countryInput.getSubsidyRates().get(crop); + if (DEBUG) LogWriter.println(String.format("%15s,\t %.4f", crop.getGamsName(), subsidyRate)); + setGamsParamValue(subsideRateP.addRecord(crop.getGamsName()), subsidyRate, 4); + } + + if (DEBUG) LogWriter.println("\nImport-export, min trade/prod, max trade/prod, global import price, global export price, imports, exports, ruminantFeed, monogastricFeed, seedAndWasteRate"); + + GAMSParameter minTradeP = null; + GAMSParameter maxTradeP = null; + minTradeP = inDB.addParameter("minNetImport", 1); + maxTradeP = inDB.addParameter("maxNetImport", 1); + + GAMSParameter importPriceP = inDB.addParameter("importPrices", 1); + GAMSParameter exportPriceP = inDB.addParameter("exportPrices", 1); + + for (CropType crop : CropType.getImportedTypes()) { + + TradeConstraint iec = countryInput.getTradeConstraints().get(crop); + CountryPrice gp = countryInput.getCountryPrices().get(crop); + double minTrade = iec.getMinConstraint(); + double maxTrade = iec.getMaxConstraint(); + double importPrice = gp.getImportPrice(); + double exportPrice = gp.getExportPrice(); + + CropUsageData cu = countryInput.getPreviousCropUsageData().get(crop); + double netImports = cu.getNetImportsExpected(); + double imports = netImports>0 ? netImports : 0; + double exports = netImports<0 ? -netImports : 0; + + double ruminantFeed = cu.getRuminantFeed(); + double monogastricFeed = cu.getMonogastricFeed(); + double seedAndWasteRate = crop.getSeedAndWasteRate(); + + if (DEBUG) LogWriter.println(String.format(" %15s, \t %5.1f, \t %5.1f, \t %5.3f, \t %5.3f, \t %5.1f, \t %5.1f, \t %5.1f, \t %5.1f, \t %5.3f", + crop.getGamsName(), minTrade, maxTrade, importPrice, exportPrice, imports, exports, ruminantFeed, monogastricFeed, seedAndWasteRate)); + + setGamsParamValue(minTradeP.addRecord(crop.getGamsName()), minTrade, 3); + setGamsParamValue(maxTradeP.addRecord(crop.getGamsName()), maxTrade, 3); + setGamsParamValue(importPriceP.addRecord(crop.getGamsName()), importPrice, 3); + setGamsParamValue(exportPriceP.addRecord(crop.getGamsName()), exportPrice, 3); + setGamsParamValue(previousImportAmountP.addRecord(crop.getGamsName()), imports, 3); + setGamsParamValue(previousExportAmountP.addRecord(crop.getGamsName()), exports, 3); + setGamsParamValue(previousRuminantFeedP.addRecord(crop.getGamsName()), ruminantFeed, 3); + setGamsParamValue(previousMonogastricFeedP.addRecord(crop.getGamsName()), monogastricFeed, 3); + setGamsParamValue(seedAndWasteRateP.addRecord(crop.getGamsName()), seedAndWasteRate, 3); + } + + + //below in case running without shocks to use original values in model config + + double meatEff = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(), "MEAT_EFFICIENCY"); + double fertCost = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(), "FERTILISER_COST_PER_T"); + double otherIntCost = ModelConfig.updateParameterForShocks(inputData.getTimestep().getYear(), "OTHER_INTENSITY_COST"); + + LogWriter.print("\n"); + addScalar(inDB, "meatEfficency", meatEff, 3); + addScalar(inDB, "fertiliserUnitCost", fertCost, 3); + addScalar(inDB, "otherICost",otherIntCost, 3); + addScalar(inDB, "otherIParam", ModelConfig.OTHER_INTENSITY_PARAM, 3); + addScalar(inDB, "unhandledCropRate", ModelConfig.UNHANDLED_CROP_RATE, 3); + addScalar(inDB, "setAsideRate", ModelConfig.SETASIDE_RATE, 5); + addScalar(inDB, "domesticPriceMarkup", ModelConfig.DOMESTIC_PRICE_MARKUP, 3); + double maxExpansion = 1.0; + if (!ModelConfig.IS_CALIBRATION_RUN && countryInput.getCountry().getName().equals("China")) { + maxExpansion = ModelConfig.MAX_CHINA_LAND_EXPANSION_RATE; + } + addScalar(inDB, "maxLandExpansionRate", maxExpansion, 3); + + addScalar(inDB, "carbonPrice", countryInput.getCarbonPrice().getExportPrice(), 5); + addScalar(inDB, "woodExportPrice", countryInput.getWoodPrice().getExportPrice(), 5); + addScalar(inDB, "woodImportPrice", countryInput.getWoodPrice().getImportPrice(), 5); + + // Not simulating production of different wood products so need to aggregate + double woodMinNetImport = 0; + double woodMaxNetImport = 0; + Map<WoodType, TradeConstraint> woodTradeConstraints = countryInput.getWoodTradeConstraints(); + for (TradeConstraint tc: woodTradeConstraints.values()) { + woodMinNetImport += tc.getMinConstraint(); + woodMaxNetImport += tc.getMaxConstraint(); + } + + addScalar(inDB, "woodMaxNetImport", woodMaxNetImport, 5); + addScalar(inDB, "woodMinNetImport", woodMinNetImport, 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("carbonFluxRateLUC", 3); + GAMSParameter cFluxRateNeeP = inDB.addParameter("carbonFluxRateNEE", 2); + + for (Entry<Integer, ? extends CarbonFluxItem> entry : inputData.getCarbonFluxes().entrySet()) { + Integer locationId = entry.getKey(); + String locString = Integer.toString(locationId); + CarbonFluxItem cFlux = entry.getValue(); + + for (Map.Entry<LccKey, Double> cfEntry : cFlux.getConversionCarbonFluxMap().entrySet()) { + LccKey key = cfEntry.getKey(); + Vector<String> v = new Vector<String>(); + v.add(key.getFromLc().getName()); + v.add(key.getToLc().getName()); + v.add(locString); + setGamsParamValue(cFluxRateP.addRecord(v), cfEntry.getValue(), 2); + } + + for (Map.Entry<LandCoverType, Double> cfEntry : cFlux.getCarbonHorizonFluxMap().entrySet()) { + Vector<String> v = new Vector<String>(); + v.add(cfEntry.getKey().getName()); + v.add(locString); + setGamsParamValue(cFluxRateNeeP.addRecord(v), cfEntry.getValue(), 3); + } + } + + // Yield from timber forest rotation + Map<Integer, ? extends WoodYieldData> woodYieldData = inputData.getWoodYields(); + + GAMSParameter woodYieldRotaP = inDB.addParameter("woodYieldRota", 1); + GAMSParameter woodYieldLuc = inDB.addParameter("woodYieldLUC", 3); + + for (Entry<Integer, ? extends WoodYieldData> entry : woodYieldData.entrySet()) { + Integer locationId = entry.getKey(); + String locString = Integer.toString(locationId); + WoodYieldData wYield = entry.getValue(); + + Vector<String> v = new Vector<String>(); + v.add(locString); + setGamsParamValue(woodYieldRotaP.addRecord(v), wYield.getYieldRota(), 5); + + for (LandCoverType toLc : LandCoverType.getAgriculturalTypes()) { + Vector<String> w = new Vector<String>(); + w.add(LandCoverType.NATURAL.getName()); + w.add(toLc.getName()); + w.add(locString); + setGamsParamValue(woodYieldLuc.addRecord(w), wYield.getYieldLuc(LandCoverType.NATURAL, toLc), 5); + } + + } + + // Land cover conversion cost + GAMSParameter conversionCostP = inDB.addParameter("conversionCost", 2); + Map<LccKey, Double> conversionCosts = inputData.getConversionCosts(); + + for (Map.Entry<LccKey, Double> entry : conversionCosts.entrySet()) { + LccKey key = entry.getKey(); + String fromName = key.getFromLc().getName(); + String toName = key.getToLc().getName(); + double cost = entry.getValue(); + Vector<String> v = new Vector<String>(); + v.add(fromName); + v.add(toName); + setGamsParamValue(conversionCostP.addRecord(v), cost, 5); + } + + GAMSParameter forestRotationP = inDB.addParameter("forestRotationPeriod", 1); + for (Map.Entry<Integer, Double> entry : inputData.getRotationPeriods().entrySet()) { + int locationId = entry.getKey(); + double rotPeriod = entry.getValue(); + String locString = Integer.toString(locationId); + setGamsParamValue(forestRotationP.addRecord(locString), rotPeriod, 0); + } + + addScalar(inDB, "forestEstablishmentCost", ModelConfig.FOREST_ESTABLISHMENT_COST, 5); + addScalar(inDB, "vegClearingCostRate", ModelConfig.VEGETATION_CLEARING_COST, 5); + } + + private void addScalar(GAMSDatabase gamsDb, String recordName, double val, int places) { + GAMSParameter param = gamsDb.addParameter(recordName, 0); + double dOut = setGamsParamValue(param.addRecord(), val, places); + if (DEBUG) LogWriter.println(recordName + ": " + dOut); + } + + private double setGamsParamValue(GAMSParameterRecord param, double val, int places) { + double dOut = places >= 0 ? Math.round(val * Math.pow(10,places)) / Math.pow(10,places) : val; + param.setValue(dOut); + return dOut; + } + + private double setGamsParamValueTruncate(GAMSParameterRecord param, double val, int places) { + double dOut = ((int)(val * Math.pow(10,places))) / Math.pow(10,places); + param.setValue(dOut); + return dOut; + } + + @SuppressWarnings("serial") + private GamsLocationOutput handleResults(GAMSDatabase outDB) { + int modelStatusInt = (int) outDB.getParameter("ms").findRecord().getValue(); + ModelStat modelStatus = GAMSGlobals.ModelStat.lookup(modelStatusInt); + String contextString = String.format("%s %s: Modelstatus %s, Solvestatus %s", + inputData.getCountryInput().getCountry(), + inputData.getTimestep().getYear(), + modelStatus.toString(), + GAMSGlobals.SolveStat.lookup((int) outDB.getParameter("ss").findRecord().getValue())); + LogWriter.println("\n" + contextString); + + if (modelStatus != ModelStat.OPTIMAL_LOCAL) { + LogWriter.printlnError("Critical!!! Land use incorrectly solved. " + contextString); + } + + GAMSVariable varAreas = outDB.getVariable("cropArea"); + GAMSVariable varFertIntensities = outDB.getVariable("fertI"); + GAMSVariable varIrrigIntensities = outDB.getVariable("irrigI"); + GAMSVariable varOtherIntensities = outDB.getVariable("otherIntensity"); + GAMSVariable varRuminantFeed = outDB.getVariable("ruminantFeed"); + GAMSVariable varMonogastricFeed = outDB.getVariable("monogastricFeed"); + GAMSParameter parmNetImports = outDB.getParameter("netImportAmount"); + GAMSParameter parmNetImportCost = outDB.getParameter("netImportCost"); + GAMSVariable varYields = outDB.getVariable("yield"); + GAMSVariable varUnitEnergies = outDB.getVariable("unitCost"); + GAMSParameter parmProd = outDB.getParameter("totalProd"); + GAMSParameter parmProdCost = outDB.getParameter("totalProdCost"); + GAMSParameter parmCroplandArea = outDB.getParameter("totalCropland"); + GAMSParameter parmTotalArea = outDB.getParameter("totalArea"); + GAMSParameter parmProdShock = outDB.getParameter("productionShock"); + + double totalCropArea = 0; + double totalPastureArea = 0; + double area, fertIntensity, irrigIntensity, otherIntensity = Double.NaN, ruminantFeed, monogastricFeed, netImport, netImportCost, yield, unitCost, prod, prodCost; + + final LazyHashMap<Integer, LandUseItem> landUses = new LazyHashMap<Integer, LandUseItem>() { + protected LandUseItem createValue() { return new LandUseItem(); } + }; + + Map<Integer, ? extends IrrigationItem> allIrrigationRefData = inputData.getIrrigationCosts(); + + Map<CropType, CropUsageData> cropUsageData = new HashMap<CropType, CropUsageData>(); + + for (GAMSVariableRecord rec : varAreas) { + String itemName = rec.getKeys()[0]; + String locationName = rec.getKeys()[1]; + area = rec.getLevel(); + fertIntensity = varFertIntensities.findRecord(itemName, locationName).getLevel(); + irrigIntensity = varIrrigIntensities.findRecord(itemName, locationName).getLevel(); + otherIntensity = varOtherIntensities.findRecord(itemName, locationName).getLevel(); + yield = varYields.findRecord(itemName, locationName).getLevel(); + unitCost = varUnitEnergies.findRecord(itemName, locationName).getLevel(); + + int locId = Integer.parseInt(locationName); + CropType cropType = CropType.getForGamsName(itemName); + + if (!cropUsageData.containsKey(cropType)) { // then we must not have seen this crop type before, so need to do all non location specific stuff + ruminantFeed = varRuminantFeed.findRecord(itemName).getLevel(); + monogastricFeed = varMonogastricFeed.findRecord(itemName).getLevel(); + netImport = cropType.isImportedCrop() ? getParmValue(parmNetImports, itemName) : 0; + netImportCost = cropType.isImportedCrop() ? getParmValue(parmNetImportCost, itemName) : 0; + prod = getParmValue(parmProd, itemName); + prodCost = getParmValue(parmProdCost, itemName); + double totalArea = getParmValue(parmTotalArea, itemName); + double prodShock = getParmValue(parmProdShock, itemName); + + cropUsageData.put(cropType, new CropUsageData(ruminantFeed, monogastricFeed, netImport, netImportCost, prod, prodCost, totalArea, prodShock)); + if (DEBUG) LogWriter.println(String.format("\n%s:\tarea= %.1f,\tmonogastricFeed= %.1f,\truminantFeed= %.1f,\tnetImports= %.3f,\tnetImportCost= %.3f,\tprod= %.3f, \tprodCost= %.3f,\tprodCostRate= %.3f,\tprodShock= %.3f", itemName, totalArea, monogastricFeed, ruminantFeed, netImport, netImportCost, prod, prodCost, prodCost/prod, prodShock)); + + } + + LandUseItem landUseItem = landUses.lazyGet(locId); // returns landUseItem for location. If does not exist, creates new one + + if (area > 0) { + + if (DEBUG) LogWriter.println(String.format("\t location %s, %s:\tarea= %.1f,\tfert= %.3f,\tirrg= %.3f,\tintensity= %.3f", locationName, itemName, area, fertIntensity, irrigIntensity, otherIntensity)); + IrrigationItem irrigRefData = allIrrigationRefData.get(locId); + landUseItem.setIntensity(cropType, new Intensity(fertIntensity, irrigIntensity, otherIntensity, yield, unitCost, irrigRefData.getMaxIrrigAmount(cropType))); + } + + + double croplandArea = getParmValue(parmCroplandArea, locationName); + + landUseItem.setCropFraction(cropType, croplandArea > 0 ? area/croplandArea : 0); // TODO gives wrong pasture numbers + + } + + for (CropType meatTypes : CropType.getMeatTypes()) { + netImport = getParmValue(parmNetImports, meatTypes.getGamsName()); + netImportCost= getParmValue(parmNetImportCost, meatTypes.getGamsName()); + prod = getParmValue(parmProd, meatTypes.getGamsName()); + prodCost = getParmValue(parmProdCost, meatTypes.getGamsName()); + + cropUsageData.put(meatTypes, new CropUsageData(0.0, 0.0, netImport, netImportCost, prod, prodCost, Double.NaN, 0)); + if (DEBUG) LogWriter.println(String.format("\n%s:\t\t\t\t\tnetImports= %.3f,\tnetImportCost= %.3f,\tprod= %.3f,\tprodCost= %.3f", meatTypes.getGamsName(), netImport, netImportCost, prod, prodCost)); + } + LogWriter.println(String.format("\n%s %s: Total area= %.1f (crop=%.1f, pasture %.1f)", + inputData.getCountryInput().getCountry(), inputData.getTimestep().getYear(), + totalCropArea+totalPastureArea, totalCropArea, totalPastureArea)); + + // Land cover change + GAMSVariable varLandCoverChange = outDB.getVariable("landCoverChange"); + Map<Integer, ArrayList<LandCoverChangeItem>> landCoverChanges = new HashMap<Integer, ArrayList<LandCoverChangeItem>>(); + + for (GAMSVariableRecord rec : varLandCoverChange) { + String fromLcStr = rec.getKeys()[0]; + String toLcStr = rec.getKeys()[1]; + String locationName = rec.getKeys()[2]; + int locId = Integer.parseInt(locationName); + double change = rec.getLevel(); + + // Skip if no change or no conversion + if (change == 0 || fromLcStr.equals(toLcStr)) + continue; + + LandCoverType fromLc = LandCoverType.getForName(fromLcStr); + LandCoverType toLc = LandCoverType.getForName(toLcStr); + + ArrayList<LandCoverChangeItem> changesList = landCoverChanges.computeIfAbsent(locId, k -> new ArrayList<LandCoverChangeItem>()); + changesList.add(new LandCoverChangeItem(fromLc, toLc, change)); + } + + // Carbon flux + double netCarbonFlux = outDB.getParameter("netCarbonFlux").getFirstRecord().getValue(); + + // Timber harvest + double totalWoodHarvest = outDB.getParameter("totalWoodHarvest").getFirstRecord().getValue(); + double netWoodImport = outDB.getParameter("netWoodImport").getFirstRecord().getValue(); + double lucHarvest = outDB.getVariable("woodSupplyLUC").getFirstRecord().getLevel(); + + // Need to disaggregate wood harvest and import by type. This is just for future proofing. Eventually, could have separate harvest for each wood type. + Map<WoodType, WoodUsageData> newWoodUsageMap = new HashMap<WoodType, WoodUsageData>(); + Map<WoodType, WoodUsageData> previousWoodUsageMap = inputData.getCountryInput().getPreviousWoodUsageData(); + double previousNetImportSum = previousWoodUsageMap.values().stream().mapToDouble(o -> o.getNetImport()).sum(); + double netWoodImportChange = netWoodImport - previousNetImportSum; + double previousWoodHarvestSum = previousWoodUsageMap.values().stream().mapToDouble(o -> o.getHarvest()).sum(); + double previousLucHavrestSum = previousWoodUsageMap.values().stream().mapToDouble(o -> o.getLucHarvest()).sum(); + int numWoodTypes = WoodType.values().length; + for (WoodType wt : WoodType.values()) { + double previousNetImport = previousWoodUsageMap.get(wt).getNetImport(); + double newNetImport = previousNetImport + netWoodImportChange / numWoodTypes; + double previousHarvest = previousWoodUsageMap.get(wt).getHarvest(); + double previousLucHarvest = previousWoodUsageMap.get(wt).getLucHarvest(); + // assuming equal split if no previous harvest + double newHarvest = (previousWoodHarvestSum > 0) ? totalWoodHarvest * (previousHarvest / previousWoodHarvestSum) : totalWoodHarvest / numWoodTypes; + double newLucHarvest = (previousLucHavrestSum > 0) ? lucHarvest * (previousLucHarvest / previousLucHavrestSum) : lucHarvest / numWoodTypes; + newWoodUsageMap.put(wt, new WoodUsageData(newHarvest, newNetImport, newLucHarvest)); + } + + GamsLocationOutput results = new GamsLocationOutput(modelStatus, landUses, cropUsageData, landCoverChanges, netCarbonFlux, newWoodUsageMap); + return results; + } + + private double getParmValue(GAMSParameter aParm, String itemName) { + try { + GAMSParameterRecord record = aParm.findRecord(itemName); + double d = record.getValue(); + return d; + } + catch (GAMSException gamsEx) { + //LogWriter.println("GAMSException thrown for " + itemName); + return 0; + } + } + + private void addCommodityMapParm(GAMSParameter parm, Map<CommodityType, Double> itemMap, int places) { + for (Map.Entry<CommodityType, Double> entry : itemMap.entrySet()) { + double d = entry.getValue(); + if (DEBUG) LogWriter.println(String.format(" %15s,\t %.1f", entry.getKey().getGamsName(), d)); + if (!Double.isNaN(d)) + setGamsParamValue(parm.addRecord(entry.getKey().getGamsName()), d, places); + } + } + + + 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); + } + } + try { + directoryToDelete.delete(); + } catch(Exception e) { + LogWriter.print(e); + } + } +} diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOutput.java b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java index 08bb3f85a592941e798f553c7413daea5e7eac2e..5b2d3b4ab47d2355cc7e65d3210cf21d5b53e397 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOutput.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOutput.java @@ -1,67 +1,60 @@ -package ac.ed.lurg.country.gams; - -import java.util.ArrayList; -import java.util.Map; - -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.types.WoodType; -import ac.ed.lurg.country.LandCoverChangeItem; - -public class GamsLocationOutput { - ModelStat status; - - Map<Integer, LandUseItem> landUses; // data mapped from id (not raster) - private Map<CropType, CropUsageData> cropUsageData; - Map<Integer, ArrayList<LandCoverChangeItem>> landCoverChanges; - private double netCarbonFlux; - private Map<WoodType, WoodUsageData> woodUsageData; - private Map<Integer, Double> naturalForestCleared; - - public GamsLocationOutput(ModelStat status, - Map<Integer, LandUseItem> landUses, - Map<CropType, CropUsageData> cropUsageData, - Map<Integer, ArrayList<LandCoverChangeItem>> landCoverChanges, - double netCarbonFlux, Map<WoodType, WoodUsageData> woodUsageData, - Map<Integer, Double> naturalForestCleared) { - super(); - this.status = status; - this.landUses = landUses; - this.cropUsageData = cropUsageData; - this.landCoverChanges = landCoverChanges; - this.netCarbonFlux = netCarbonFlux; - this.woodUsageData = woodUsageData; - this.naturalForestCleared = naturalForestCleared; - } - - public ModelStat getStatus() { - return status; - } - public Map<Integer, LandUseItem> getLandUses() { - return landUses; - } - - public Map<CropType, CropUsageData> getCommoditiesData() { - return cropUsageData; - } - - public Map<Integer, ArrayList<LandCoverChangeItem>> getLandCoverChanges() { - return landCoverChanges; - } - - public double getNetCarbonFlux() { - return netCarbonFlux; - } - - public Map<WoodType, WoodUsageData> getWoodUsageData() { - return woodUsageData; - } - - public Map<Integer, Double> getNaturalForestCleared() { - return naturalForestCleared; - } -} +package ac.ed.lurg.country.gams; + +import java.util.ArrayList; +import java.util.Map; + +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.types.WoodType; +import ac.ed.lurg.country.LandCoverChangeItem; + +public class GamsLocationOutput { + ModelStat status; + + Map<Integer, LandUseItem> landUses; // data mapped from id (not raster) + private Map<CropType, CropUsageData> cropUsageData; + Map<Integer, ArrayList<LandCoverChangeItem>> landCoverChanges; + private double netCarbonFlux; + private Map<WoodType, WoodUsageData> woodUsageData; + + public GamsLocationOutput(ModelStat status, + Map<Integer, LandUseItem> landUses, + Map<CropType, CropUsageData> cropUsageData, + Map<Integer, ArrayList<LandCoverChangeItem>> landCoverChanges, + double netCarbonFlux, Map<WoodType, WoodUsageData> woodUsageData) { + super(); + this.status = status; + this.landUses = landUses; + this.cropUsageData = cropUsageData; + this.landCoverChanges = landCoverChanges; + this.netCarbonFlux = netCarbonFlux; + this.woodUsageData = woodUsageData; + } + + public ModelStat getStatus() { + return status; + } + public Map<Integer, LandUseItem> getLandUses() { + return landUses; + } + + public Map<CropType, CropUsageData> getCommoditiesData() { + return cropUsageData; + } + + public Map<Integer, ArrayList<LandCoverChangeItem>> getLandCoverChanges() { + return landCoverChanges; + } + + public double getNetCarbonFlux() { + return netCarbonFlux; + } + + public Map<WoodType, 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 9350524b75636f6804b27df741e132f3159582a2..a754cc60412f9ff60461fa6dba979650fd4e4094 100644 --- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java @@ -1,457 +1,453 @@ -package ac.ed.lurg.country.gams; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.carbon.CarbonFluxItem; -import ac.ed.lurg.country.LandCoverChangeItem; -import ac.ed.lurg.forestry.WoodYieldData; -import ac.ed.lurg.forestry.WoodYieldItem; -import ac.ed.lurg.landuse.Intensity; -import ac.ed.lurg.landuse.IrrigationItem; -import ac.ed.lurg.landuse.LandUseItem; -import ac.ed.lurg.landuse.LccKey; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.YieldType; -import ac.ed.lurg.utils.LazyTreeMap; -import ac.ed.lurg.utils.LogWriter; -import ac.ed.lurg.yield.YieldRaster; -import ac.ed.lurg.yield.YieldResponsesItem; -import ac.sac.raster.IntegerRasterItem; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -public class GamsRasterOptimiser { - public static final boolean DEBUG = false; - - private GamsRasterInput rasterInputData; - private RasterSet<IntegerRasterItem> mapping; - - public GamsRasterOptimiser(GamsRasterInput rasterInputData, RasterSet<IntegerRasterItem> clusterMapping) { - this.rasterInputData = rasterInputData; - this.mapping = clusterMapping; - } - - public GamsRasterOutput run() { - // workout similar areas - GamsLocationInput gamsInput = convertFromRaster(rasterInputData); - - long start = System.currentTimeMillis(); - - GamsLocationOptimiser opti = new GamsLocationOptimiser(gamsInput); - GamsLocationOutput gamsOutput = opti.run(); - - long last = System.currentTimeMillis(); - LogWriter.println("Took " + (last-start)); - - // map results back to raster - return convertToRaster(gamsInput, gamsOutput); - } - - private GamsRasterOutput convertToRaster(GamsLocationInput gamsInput, GamsLocationOutput gamsOutput) { - RasterSet<LandUseItem> newIntensityRaster = allocAreas(gamsInput.getPreviousLandUse(), gamsOutput, gamsInput.getTimestep().getYear()); - - return new GamsRasterOutput(gamsOutput.getStatus(), newIntensityRaster, gamsOutput.getCommoditiesData(), - gamsOutput.getNetCarbonFlux(), gamsOutput.getWoodUsageData()); - } - - private RasterSet<LandUseItem> createWithSameLandCovers(RasterSet<LandUseItem> toCopy) { - RasterSet<LandUseItem> theCopy = new RasterSet<LandUseItem>(toCopy.getHeaderDetails()); // TODO why do we make a copy??? - - for (Entry<RasterKey, LandUseItem> entry : toCopy.entrySet()) { - if (entry.getValue() != null) { - LandUseItem newAreasItem = new LandUseItem(entry.getValue()); - theCopy.put(entry.getKey(), newAreasItem); - } - } - - return theCopy; - } - - private void checkedTotalAreas(Map<? extends Object, LandUseItem> areaRaster, String comment) { - for (LandCoverType l : LandCoverType.values()) { - double total = 0; - double unprotected = 0; - for (LandUseItem a : areaRaster.values()) { - if (a != null) { - total += a.getConvertibleLandCoverArea(l) + a.getProtectedLandCoverArea(l); - unprotected += a.getConvertibleLandCoverArea(l); - } - - } - - LogWriter.println("Total Area " + comment + ": " + l.getName() + ": total = " + total + ", unprotected = " + unprotected); - } - - double suitableArea=0, protectedArea=0; - for (LandUseItem a : areaRaster.values()) { - if (a != null) { - protectedArea += a.getTotalProtectedArea(); - suitableArea += a.getSuitableArea(); - } - } - LogWriter.println("Total protectedArea " + comment + ": " + protectedArea); - LogWriter.println("Total suitableArea " + comment + ": " + suitableArea); - } - - private RasterSet<LandUseItem> allocAreas(Map<Integer, ? extends LandUseItem> prevAreasAgg, GamsLocationOutput gamsOutput, int year) { - RasterSet<LandUseItem> newLandUseRaster = createWithSameLandCovers(rasterInputData.getPreviousLandUses()); - - for (Map.Entry<Integer, LandUseItem> entry : gamsOutput.getLandUses().entrySet()) { - Integer locId = entry.getKey(); - LandUseItem newLandUseAggItem = entry.getValue(); - - Set<RasterKey> keys = new HashSet<RasterKey>(); - for (Entry<RasterKey, IntegerRasterItem> mapEntry : mapping.entrySet()) { - IntegerRasterItem iri = mapEntry.getValue(); - - if (iri != null && locId == iri.getInt()) - keys.add(mapEntry.getKey()); - } - - RasterSet<LandUseItem> landUseItemsForLocation = newLandUseRaster.createSubsetForKeys(keys); - - ArrayList<LandCoverChangeItem> landCoverChanges = gamsOutput.getLandCoverChanges().get(locId); - - if (landCoverChanges != null) { - // Allocate land cover change to grid cells - for (LandCoverChangeItem lccItem : landCoverChanges) { - LandCoverType fromLC = lccItem.getFromLandCover(); - LandCoverType toLC = lccItem.getToLandCover(); - double change = lccItem.getArea(); - allocAllLandCropsTop(newLandUseRaster, keys, toLC, fromLC, change, locId); - - } - } - - // Deforested natural areas. No change in natural area but forest stand area decreased and new stand created - if (gamsOutput.getNaturalForestCleared().containsKey(locId)) { - double natForestCleared = gamsOutput.getNaturalForestCleared().get(locId); - - allocAllLandCropsTop(newLandUseRaster, keys, LandCoverType.NATURAL, LandCoverType.NATURAL, natForestCleared, locId); - } - - - for (RasterKey key : keys) { - LandUseItem newLandUseItem = newLandUseRaster.get(key); - if (newLandUseItem != null) { - for (CropType crop : CropType.values()) { - newLandUseItem.setCropFraction(crop, newLandUseAggItem.getCropFraction(crop)); - newLandUseItem.setIntensity(crop, newLandUseAggItem.getIntensity(crop)); // intensities constant over single aggregated land category - } - } - } - - if (DEBUG) checkedTotalAreas(landUseItemsForLocation, locId + " after"); - } - - return newLandUseRaster; - } - - private void allocAllLandCropsTop(RasterSet<LandUseItem> newLandUseRaster, Set<RasterKey> keys, LandCoverType toLC, LandCoverType fromLC, double change, Integer locId) { - LandCoverType toType = toLC; - LandCoverType fromType = fromLC; - - // reverse direction if negative - if (change < 0) { - change = -change; - toType = fromLC; - fromType = toLC; - } - - double shortfall = allocAllLandCrops(newLandUseRaster, keys, toType, fromType, change); - - if (shortfall > 0.00001) { - LogWriter.printlnError("This should never happen, due to GAMS constraint. Not able to incorporate all changes: from " + fromLC + " to " + toLC + " " + locId + ": " + shortfall); - } - } - - private double allocAllLandCrops(RasterSet<LandUseItem> newLandUseRaster, Set<RasterKey> keys, LandCoverType toType, LandCoverType fromType, double change) { - if (DEBUG) LogWriter.println("allocAllLandCrops: from " + fromType + " to " + toType + " change " + change); - if (change < 0.00001) - return 0; - - double totalShortfall = 0; - Set<RasterKey> keysWithSpace = new HashSet<RasterKey>(); - double totalUnprotectedLC = 0; - - for (RasterKey key : keys) { - LandUseItem newLandUseItem = newLandUseRaster.get(key); - totalUnprotectedLC += newLandUseItem.getConvertibleLandCoverArea(fromType); - } - - for (RasterKey key : keys) { - //if (DEBUG) LogWriter.println("Processing raster key " + key); - LandUseItem newLandUseItem = newLandUseRaster.get(key); - - if (newLandUseItem!=null) { - double cellChange = (totalUnprotectedLC > 0) ? change * newLandUseItem.getConvertibleLandCoverArea(fromType)/totalUnprotectedLC : change / keys.size(); - double shortfall = newLandUseItem.moveAreas(toType, fromType, cellChange); - if (shortfall == 0) - keysWithSpace.add(key); - else - totalShortfall += shortfall; - - //LogWriter.println(key.toString() + "|" + cellChange); - } - } - - if (DEBUG) LogWriter.println("allocAllLandCrops: from " + fromType + " to " + toType + " change " + change + " totalShortfall " + totalShortfall); - return totalShortfall; - } - - @SuppressWarnings("serial") - private GamsLocationInput convertFromRaster(GamsRasterInput rasterInputData) { - // as a first attempt only going to look at pasture and cereal yields, assuming other yields will be correlated to one or the other. - YieldRaster yieldRaster = rasterInputData.getYields(); - RasterSet<LandUseItem> landUseRaster = rasterInputData.getPreviousLandUses(); - - RasterSet<IrrigationItem> irrigRaster = rasterInputData.getIrrigationData(); - - RasterSet<CarbonFluxItem> carbonFluxRaster = rasterInputData.getCarbonFluxes(); - - RasterSet<WoodYieldItem> woodYieldRaster = rasterInputData.getWoodYields(); - - // look for inconsistencies - for (Entry<RasterKey, YieldResponsesItem> entry : yieldRaster.entrySet()) { - - YieldResponsesItem yresp = entry.getValue(); - RasterKey key = entry.getKey(); - CropType crop = CropType.WHEAT; - - if (yresp != null && (yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) < yresp.getYield(YieldType.FERT_MAX_NO_IRRIG, crop) || yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) < yresp.getYield(YieldType.NO_FERT_IRRIG_MAX, crop))) { - logWarningWithCoord("Inconsistency F only:" + yresp.getYield(YieldType.FERT_MAX_NO_IRRIG, crop) + ", I only" + yresp.getYield(YieldType.NO_FERT_IRRIG_MAX, crop) + ", max " + yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) + " at ", key, yieldRaster); - } - } - - // Aggregation - LazyTreeMap<Integer, YieldResponsesItem> aggregatedYields = new LazyTreeMap<Integer, YieldResponsesItem>() { - protected YieldResponsesItem createValue() { return new YieldResponsesItem(); } - }; - LazyTreeMap<Integer, LandUseItem> aggregatedAreas = new LazyTreeMap<Integer, LandUseItem>() { - protected LandUseItem createValue() { return new LandUseItem(); } - }; - LazyTreeMap<Integer, IrrigationItem> aggregatedIrrigCosts = new LazyTreeMap<Integer, IrrigationItem>() { - protected IrrigationItem createValue() { return new IrrigationItem(); } - }; - LazyTreeMap<Integer, CarbonFluxItem> aggregatedCarbonFluxes = new LazyTreeMap<Integer, CarbonFluxItem>() { - protected CarbonFluxItem createValue() { return new CarbonFluxItem(); } - }; - LazyTreeMap<Integer, WoodYieldData> aggregatedWoodYields = new LazyTreeMap<Integer, WoodYieldData>() { - protected WoodYieldData createValue() { return new WoodYieldData(); } - }; - Map<Integer, Double> aggregatedForestRotation = new HashMap<Integer, Double>(); - - int yRespFound = 0, yRespMissing = 0; - int mappingsFound = 0, mappingsMissing = 0; - - for (RasterKey key : landUseRaster.keySet()) { - if (mapping.get(key) == null) { - mappingsMissing++; - continue; - } else { - mappingsFound++; - } - - int clusterId = mapping.get(key).getInt(); - - YieldResponsesItem yresp = yieldRaster.get(key); - if (yresp == null) { - yRespMissing++; - //logErrorWithCoord("No YieldResponsesItem for ", key, yieldRaster); - } - else { - yRespFound++; - //logErrorWithCoord("YieldResponsesItem found for ", key, yieldRaster); - } - - LandUseItem landUseItem = landUseRaster.get(key); - if (landUseItem == null) { - LogWriter.printlnError("GamsRasterOptimiser: Can't find landUseItem for " + key); - continue; - } - - IrrigationItem irrigItem = irrigRaster.get(key); - WoodYieldItem woodYieldItem = woodYieldRaster.get(key); - CarbonFluxItem carbonFluxItem = carbonFluxRaster.get(key); - - YieldResponsesItem aggYResp = aggregatedYields.lazyGet(clusterId); - LandUseItem aggLandUse = aggregatedAreas.lazyGet(clusterId); - IrrigationItem aggIrig = aggregatedIrrigCosts.lazyGet(clusterId); - WoodYieldData aggWYield = aggregatedWoodYields.lazyGet(clusterId); - CarbonFluxItem aggCFlux = aggregatedCarbonFluxes.lazyGet(clusterId); - - // TODO disabled protected area update. Should probably be done in CountryAgent anyway - //landUseItem.updateSuitableArea(rasterInputData.getTimestep().getYear()); - double suitableAreaThisTime = landUseItem.getSuitableArea(); - double suitableAreaSoFar = aggLandUse.getSuitableArea(); - - // Irrigation cost - if (irrigItem!= null) { - aggIrig.setIrrigCost( aggregateMean(aggIrig.getIrrigCost(), suitableAreaSoFar, irrigItem.getIrrigCost(), suitableAreaThisTime)); - aggIrig.setIrrigConstraint(aggregateMean(aggIrig.getIrrigConstraint(), suitableAreaSoFar, irrigItem.getIrrigConstraint(), suitableAreaThisTime)); - //LogWriter.println(id + ", " + key + ", " + avgIrigCost.getIrrigCost() + ", from " + areaSoFar + ", " + suitableAreaThisTime + ", " + irrigCost.getIrrigCost()); - } - - // Aggregate carbon fluxes and wood yields - if (carbonFluxItem != null) { - for (Map.Entry<LccKey, Double> cfEntry : carbonFluxItem.getConversionCarbonFluxMap().entrySet()) { - LccKey lccKey = cfEntry.getKey(); - double cFluxThisTime = cfEntry.getValue(); - double cFluxSoFar = aggCFlux.getConversionCarbonFlux(lccKey); - double areaThisTime = landUseItem.getLandCoverArea(lccKey.getFromLc()); - double areaSoFar = aggLandUse.getLandCoverArea(lccKey.getFromLc()); - double cFluxAgg = aggregateMean(cFluxSoFar, areaSoFar, cFluxThisTime, areaThisTime); - aggCFlux.setConversionCarbonFlux(lccKey, cFluxAgg); - } - - for (Map.Entry<LandCoverType, Double> cfEntry : carbonFluxItem.getEcosystemCarbonFluxMap().entrySet()) { - LandCoverType lcType = cfEntry.getKey(); - double cFluxThisTime = cfEntry.getValue(); - double cFluxSoFar = aggCFlux.getEcosystemCarbonFlux(lcType); - double areaThisTime = landUseItem.getLandCoverArea(lcType); - double areaSoFar = aggLandUse.getLandCoverArea(lcType); - double cFluxAgg = aggregateMean(cFluxSoFar, areaSoFar, cFluxThisTime, areaThisTime); - aggCFlux.setEcosystemCarbonFlux(lcType, cFluxAgg); - } - } - /* - if (woodYieldItem == null) { - LogWriter.println(""+landUseRaster.getYCoordin(key)+" "+landUseRaster.getXCoordin(key)); - } - */ - if (woodYieldItem != null) { - for (Map.Entry<LccKey, Double> wyEntry : woodYieldItem.getYieldMap().entrySet()) { - LccKey lccKey = wyEntry.getKey(); - double wYieldThisTime = wyEntry.getValue(); - double wYieldSoFar = aggWYield.getYieldLuc(lccKey); - double areaThisTime = landUseItem.getLandCoverArea(lccKey.getFromLc()); - double areaSoFar = aggLandUse.getLandCoverArea(lccKey.getFromLc()); - double wYieldAgg = aggregateMean(wYieldSoFar, areaSoFar, wYieldThisTime, areaThisTime); - aggWYield.setYieldLuc(lccKey, wYieldAgg); - } - - { - // Rotation wood yield - double wYieldThisTime = woodYieldItem.getYieldAtRotation(); - double wYieldSoFar = aggWYield.getYieldRota(); - double wYieldAgg = aggregateMean(wYieldSoFar, suitableAreaSoFar, wYieldThisTime, suitableAreaThisTime); - aggWYield.setYieldRota(wYieldAgg); - } - - - // Rotation period - double forestRotaThisTime = woodYieldItem.getOptimalRotation(); - double forestRotaSoFar = (aggregatedForestRotation.containsKey(clusterId)) ? aggregatedForestRotation.get(clusterId) : forestRotaThisTime; - double forestRotaAgg = aggregateMean(forestRotaSoFar, suitableAreaSoFar, forestRotaThisTime, suitableAreaThisTime); - aggregatedForestRotation.put(clusterId, forestRotaAgg); - } - - // Crops yields and area fractions - if (yresp != null) { - for (CropType crop : CropType.getNonMeatTypes()) { - if (!crop.equals(CropType.SETASIDE)) { - if (irrigItem!= null) { - double irrigMax = irrigItem.getMaxIrrigAmount(crop); - - if (Double.isNaN(irrigMax)) - logWarningWithCoord("Can't find irrig max amount for ", key, yieldRaster, crop); - else - aggIrig.setMaxIrrigAmount(crop, aggregateMean(aggIrig.getMaxIrrigAmount(crop), suitableAreaSoFar, irrigMax, suitableAreaThisTime)); - } - - for (YieldType yieldType : YieldType.values()) { - double y = yresp.getYield(yieldType, crop); - if (Double.isNaN(y)) - logWarningWithCoord("Problem getting yield for type:" + yieldType + ", ", key, yieldRaster, crop); - else { - double yieldSoFar = aggYResp.getYield(yieldType, crop); - double updatedYield = aggregateMean(yieldSoFar, suitableAreaSoFar, y, suitableAreaThisTime); - aggYResp.setYield(yieldType, crop, updatedYield); - } - } - - double s = yresp.getShockRate(crop); - if (!Double.isNaN(s)) { - double shockSoFar = aggYResp.getShockRate(crop); - double updatedShock = aggregateMean(shockSoFar, suitableAreaSoFar, s, suitableAreaThisTime); - aggYResp.setShockRate(crop, updatedShock); - } - } - - double areaSoFar = aggLandUse.getCropArea(crop); - double areaThisTime = landUseItem.getCropArea(crop); - double updateCropland = (landUseItem.getLandCoverArea(LandCoverType.CROPLAND) + aggLandUse.getLandCoverArea(LandCoverType.CROPLAND)); - - if (updateCropland > 0) - aggLandUse.setCropFraction(crop, (areaSoFar + areaThisTime) / updateCropland); - - Intensity intensityThisTime = landUseItem.getIntensity(crop); - aggLandUse.setIntensity(crop, intensityThisTime); // intensity currently is always the same within a location, so can just that any. If this changes need to average here. - } - } - - // Land covers areas - for (LandCoverType lcType : LandCoverType.values()) { - double convAreaThisTime = landUseItem.getConvertibleLandCoverArea(lcType); - double convAreaSoFar = aggLandUse.getConvertibleLandCoverArea(lcType); - aggLandUse.setConvertibleLandCoverArea(lcType, convAreaSoFar + convAreaThisTime); - - double protAreaThisTime = landUseItem.getProtectedLandCoverArea(lcType); - double protAreaSoFar = aggLandUse.getProtectedLandCoverArea(lcType); - aggLandUse.setProtectedLandCoverArea(lcType, protAreaSoFar + protAreaThisTime); - } - - - } - - LogWriter.println("Mapping: " + rasterInputData.getCountryInput().getCountry() + ", countFound=" + mappingsFound + ", countMissing=" + mappingsMissing); - LogWriter.println("YieldResponsesItem: " + rasterInputData.getCountryInput().getCountry() + ", countFound=" + yRespFound + ", countMissing=" + yRespMissing); - - checkedTotalAreas(landUseRaster, "before"); - checkedTotalAreas(aggregatedAreas, "after"); - - - if (!ModelConfig.IS_FORESTRY_ON) { - for (Integer locId : aggregatedYields.keySet()) { - aggregatedForestRotation.put(locId, 1.0); // shouldn't matter what this is as long as it's >0 - } - } - - // TODO shouldn't have missing data in the first place - double meanRotation = aggregatedForestRotation.values().stream().mapToDouble(o -> o.doubleValue()).sum() / aggregatedForestRotation.keySet().size(); - for (Integer locId : aggregatedYields.keySet()) { - if (!aggregatedForestRotation.containsKey(locId)) { - aggregatedForestRotation.put(locId, meanRotation); - LogWriter.printlnWarning("GamsRasterOptimister.convertFromRaster missing timber rotation; substituting average"); - } - } - - return new GamsLocationInput(rasterInputData.getTimestep(), aggregatedYields, aggregatedAreas, aggregatedIrrigCosts, - aggregatedCarbonFluxes, aggregatedWoodYields, rasterInputData.getConversionCosts(), rasterInputData.getCountryInput(), aggregatedForestRotation); - } - - private void logWarningWithCoord(String message, RasterKey key, YieldRaster yieldRaster, CropType crop) { - logWarningWithCoord(message + "crop:" + crop + ", ", key, yieldRaster); - } - - private void logWarningWithCoord(String message, RasterKey key, YieldRaster yieldRaster) { - LogWriter.printlnWarning(message + key + ", x:" + yieldRaster.getXCoordin(key) + ", y:" + yieldRaster.getYCoordin(key)); - } - - private double aggregateMean(double aggV, double aggArea, double newV, double newArea) { - if (Double.isNaN(aggV)) - return newV; - - if (newArea == 0) - return aggV; - - return (aggV*aggArea + newV*newArea) / (aggArea + newArea); - } -} +package ac.ed.lurg.country.gams; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.carbon.CarbonFluxItem; +import ac.ed.lurg.country.LandCoverChangeItem; +import ac.ed.lurg.forestry.WoodYieldData; +import ac.ed.lurg.forestry.WoodYieldItem; +import ac.ed.lurg.landuse.Intensity; +import ac.ed.lurg.landuse.IrrigationItem; +import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.landuse.LccKey; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.ed.lurg.types.YieldType; +import ac.ed.lurg.utils.LazyTreeMap; +import ac.ed.lurg.utils.LogWriter; +import ac.ed.lurg.yield.YieldRaster; +import ac.ed.lurg.yield.YieldResponsesItem; +import ac.sac.raster.IntegerRasterItem; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterSet; + +public class GamsRasterOptimiser { + public static final boolean DEBUG = false; + + private GamsRasterInput rasterInputData; + private RasterSet<IntegerRasterItem> mapping; + + public GamsRasterOptimiser(GamsRasterInput rasterInputData, RasterSet<IntegerRasterItem> clusterMapping) { + this.rasterInputData = rasterInputData; + this.mapping = clusterMapping; + } + + public GamsRasterOutput run() { + // workout similar areas + GamsLocationInput gamsInput = convertFromRaster(rasterInputData); + + long start = System.currentTimeMillis(); + + GamsLocationOptimiser opti = new GamsLocationOptimiser(gamsInput); + GamsLocationOutput gamsOutput = opti.run(); + + long last = System.currentTimeMillis(); + LogWriter.println("Took " + (last-start)); + + // map results back to raster + return convertToRaster(gamsInput, gamsOutput); + } + + private GamsRasterOutput convertToRaster(GamsLocationInput gamsInput, GamsLocationOutput gamsOutput) { + RasterSet<LandUseItem> newIntensityRaster = allocAreas(gamsInput.getPreviousLandUse(), gamsOutput, gamsInput.getTimestep().getYear()); + + return new GamsRasterOutput(gamsOutput.getStatus(), newIntensityRaster, gamsOutput.getCommoditiesData(), + gamsOutput.getNetCarbonFlux(), gamsOutput.getWoodUsageData()); + } + + private RasterSet<LandUseItem> createWithSameLandCovers(RasterSet<LandUseItem> toCopy) { + RasterSet<LandUseItem> theCopy = new RasterSet<LandUseItem>(toCopy.getHeaderDetails()); // TODO why do we make a copy??? + + for (Entry<RasterKey, LandUseItem> entry : toCopy.entrySet()) { + if (entry.getValue() != null) { + LandUseItem newAreasItem = new LandUseItem(entry.getValue()); + theCopy.put(entry.getKey(), newAreasItem); + } + } + + return theCopy; + } + + private void checkedTotalAreas(Map<? extends Object, LandUseItem> areaRaster, String comment) { + for (LandCoverType l : LandCoverType.values()) { + double total = 0; + double unprotected = 0; + for (LandUseItem a : areaRaster.values()) { + if (a != null) { + total += a.getLandCoverArea(l, LandProtectionType.CONVERTIBLE) + a.getLandCoverArea(l, LandProtectionType.PROTECTED); + unprotected += a.getLandCoverArea(l, LandProtectionType.CONVERTIBLE); + } + + } + + LogWriter.println("Total Area " + comment + ": " + l.getName() + ": total = " + total + ", unprotected = " + unprotected); + } + + double suitableArea=0, protectedArea=0; + for (LandUseItem a : areaRaster.values()) { + if (a != null) { + protectedArea += a.getTotalLandCoverArea(LandProtectionType.PROTECTED); + suitableArea += a.getSuitableArea(); + } + } + LogWriter.println("Total protectedArea " + comment + ": " + protectedArea); + LogWriter.println("Total suitableArea " + comment + ": " + suitableArea); + } + + private RasterSet<LandUseItem> allocAreas(Map<Integer, ? extends LandUseItem> prevAreasAgg, GamsLocationOutput gamsOutput, int year) { + RasterSet<LandUseItem> newLandUseRaster = createWithSameLandCovers(rasterInputData.getPreviousLandUses()); + + for (Map.Entry<Integer, LandUseItem> entry : gamsOutput.getLandUses().entrySet()) { + Integer locId = entry.getKey(); + LandUseItem newLandUseAggItem = entry.getValue(); + + Set<RasterKey> keys = new HashSet<RasterKey>(); + for (Entry<RasterKey, IntegerRasterItem> mapEntry : mapping.entrySet()) { + IntegerRasterItem iri = mapEntry.getValue(); + + if (iri != null && locId == iri.getInt()) + keys.add(mapEntry.getKey()); + } + + RasterSet<LandUseItem> landUseItemsForLocation = newLandUseRaster.createSubsetForKeys(keys); + + ArrayList<LandCoverChangeItem> landCoverChanges = gamsOutput.getLandCoverChanges().get(locId); + + if (landCoverChanges != null) { + // Allocate land cover change to grid cells + for (LandCoverChangeItem lccItem : landCoverChanges) { + LandCoverType fromLC = lccItem.getFromLandCover(); + LandCoverType toLC = lccItem.getToLandCover(); + double change = lccItem.getArea(); + allocAllLandCropsTop(newLandUseRaster, keys, toLC, fromLC, change, locId); + + } + } + + for (RasterKey key : keys) { + LandUseItem newLandUseItem = newLandUseRaster.get(key); + if (newLandUseItem != null) { + for (CropType crop : CropType.values()) { + newLandUseItem.setCropFraction(crop, newLandUseAggItem.getCropFraction(crop)); + newLandUseItem.setIntensity(crop, newLandUseAggItem.getIntensity(crop)); // intensities constant over single aggregated land category + } + } + } + + if (DEBUG) checkedTotalAreas(landUseItemsForLocation, locId + " after"); + } + + return newLandUseRaster; + } + + private void allocAllLandCropsTop(RasterSet<LandUseItem> newLandUseRaster, Set<RasterKey> keys, LandCoverType toLC, LandCoverType fromLC, double change, Integer locId) { + LandCoverType toType = toLC; + LandCoverType fromType = fromLC; + + // reverse direction if negative + if (change < 0) { + change = -change; + toType = fromLC; + fromType = toLC; + } + + double shortfall = allocAllLandCrops(newLandUseRaster, keys, toType, fromType, change); + + if (shortfall > 0.00001) { + LogWriter.printlnError("This should never happen, due to GAMS constraint. Not able to incorporate all changes: from " + fromLC + " to " + toLC + " " + locId + ": " + shortfall); + } + } + + private double allocAllLandCrops(RasterSet<LandUseItem> newLandUseRaster, Set<RasterKey> keys, LandCoverType toType, LandCoverType fromType, double change) { + if (DEBUG) LogWriter.println("allocAllLandCrops: from " + fromType + " to " + toType + " change " + change); + if (change < 0.00001) + return 0; + + double totalShortfall = 0; + Set<RasterKey> keysWithSpace = new HashSet<RasterKey>(); + double totalUnprotectedLC = 0; + + for (RasterKey key : keys) { + LandUseItem newLandUseItem = newLandUseRaster.get(key); + totalUnprotectedLC += newLandUseItem.getLandCoverArea(fromType, LandProtectionType.CONVERTIBLE); + } + + for (RasterKey key : keys) { + //if (DEBUG) LogWriter.println("Processing raster key " + key); + LandUseItem newLandUseItem = newLandUseRaster.get(key); + + if (newLandUseItem!=null) { + double cellChange = (totalUnprotectedLC > 0) ? change * newLandUseItem.getLandCoverArea(fromType, LandProtectionType.CONVERTIBLE)/totalUnprotectedLC : change / keys.size(); + double shortfall = newLandUseItem.moveAreas(toType, fromType, cellChange); + if (shortfall == 0) + keysWithSpace.add(key); + else + totalShortfall += shortfall; + + //LogWriter.println(key.toString() + "|" + cellChange); + } + } + + if (DEBUG) LogWriter.println("allocAllLandCrops: from " + fromType + " to " + toType + " change " + change + " totalShortfall " + totalShortfall); + return totalShortfall; + } + + @SuppressWarnings("serial") + private GamsLocationInput convertFromRaster(GamsRasterInput rasterInputData) { + // as a first attempt only going to look at pasture and cereal yields, assuming other yields will be correlated to one or the other. + YieldRaster yieldRaster = rasterInputData.getYields(); + RasterSet<LandUseItem> landUseRaster = rasterInputData.getPreviousLandUses(); + + RasterSet<IrrigationItem> irrigRaster = rasterInputData.getIrrigationData(); + + RasterSet<CarbonFluxItem> carbonFluxRaster = rasterInputData.getCarbonFluxes(); + + RasterSet<WoodYieldItem> woodYieldRaster = rasterInputData.getWoodYields(); + + // look for inconsistencies + for (Entry<RasterKey, YieldResponsesItem> entry : yieldRaster.entrySet()) { + + YieldResponsesItem yresp = entry.getValue(); + RasterKey key = entry.getKey(); + CropType crop = CropType.WHEAT; + + if (yresp != null && (yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) < yresp.getYield(YieldType.FERT_MAX_NO_IRRIG, crop) || yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) < yresp.getYield(YieldType.NO_FERT_IRRIG_MAX, crop))) { + logWarningWithCoord("Inconsistency F only:" + yresp.getYield(YieldType.FERT_MAX_NO_IRRIG, crop) + ", I only" + yresp.getYield(YieldType.NO_FERT_IRRIG_MAX, crop) + ", max " + yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) + " at ", key, yieldRaster); + } + } + + // Aggregation + LazyTreeMap<Integer, YieldResponsesItem> aggregatedYields = new LazyTreeMap<Integer, YieldResponsesItem>() { + protected YieldResponsesItem createValue() { return new YieldResponsesItem(); } + }; + LazyTreeMap<Integer, LandUseItem> aggregatedAreas = new LazyTreeMap<Integer, LandUseItem>() { + protected LandUseItem createValue() { return new LandUseItem(); } + }; + LazyTreeMap<Integer, IrrigationItem> aggregatedIrrigCosts = new LazyTreeMap<Integer, IrrigationItem>() { + protected IrrigationItem createValue() { return new IrrigationItem(); } + }; + LazyTreeMap<Integer, CarbonFluxItem> aggregatedCarbonFluxes = new LazyTreeMap<Integer, CarbonFluxItem>() { + protected CarbonFluxItem createValue() { return new CarbonFluxItem(); } + }; + LazyTreeMap<Integer, WoodYieldData> aggregatedWoodYields = new LazyTreeMap<Integer, WoodYieldData>() { + protected WoodYieldData createValue() { return new WoodYieldData(); } + }; + Map<Integer, Double> aggregatedForestRotation = new HashMap<Integer, Double>(); + + int yRespFound = 0, yRespMissing = 0; + int mappingsFound = 0, mappingsMissing = 0; + + for (RasterKey key : landUseRaster.keySet()) { + if (mapping.get(key) == null) { + mappingsMissing++; + continue; + } else { + mappingsFound++; + } + + int clusterId = mapping.get(key).getInt(); + + YieldResponsesItem yresp = yieldRaster.get(key); + if (yresp == null) { + yRespMissing++; + //logErrorWithCoord("No YieldResponsesItem for ", key, yieldRaster); + } + else { + yRespFound++; + //logErrorWithCoord("YieldResponsesItem found for ", key, yieldRaster); + } + + LandUseItem landUseItem = landUseRaster.get(key); + if (landUseItem == null) { + LogWriter.printlnError("GamsRasterOptimiser: Can't find landUseItem for " + key); + continue; + } + + IrrigationItem irrigItem = irrigRaster.get(key); + WoodYieldItem woodYieldItem = woodYieldRaster.get(key); + CarbonFluxItem carbonFluxItem = carbonFluxRaster.get(key); + + YieldResponsesItem aggYResp = aggregatedYields.lazyGet(clusterId); + LandUseItem aggLandUse = aggregatedAreas.lazyGet(clusterId); + IrrigationItem aggIrig = aggregatedIrrigCosts.lazyGet(clusterId); + WoodYieldData aggWYield = aggregatedWoodYields.lazyGet(clusterId); + CarbonFluxItem aggCFlux = aggregatedCarbonFluxes.lazyGet(clusterId); + + double suitableAreaThisTime = landUseItem.getSuitableArea(); + double suitableAreaSoFar = aggLandUse.getSuitableArea(); + + // Irrigation cost + if (irrigItem!= null) { + aggIrig.setIrrigCost( aggregateMean(aggIrig.getIrrigCost(), suitableAreaSoFar, irrigItem.getIrrigCost(), suitableAreaThisTime)); + aggIrig.setIrrigConstraint(aggregateMean(aggIrig.getIrrigConstraint(), suitableAreaSoFar, irrigItem.getIrrigConstraint(), suitableAreaThisTime)); + //LogWriter.println(id + ", " + key + ", " + avgIrigCost.getIrrigCost() + ", from " + areaSoFar + ", " + suitableAreaThisTime + ", " + irrigCost.getIrrigCost()); + } + + // Aggregate carbon fluxes and wood yields + if (carbonFluxItem != null) { + for (Map.Entry<LccKey, Double> cfEntry : carbonFluxItem.getConversionCarbonFluxMap().entrySet()) { + LccKey lccKey = cfEntry.getKey(); + double cFluxThisTime = cfEntry.getValue(); + double cFluxSoFar = aggCFlux.getConversionCarbonFlux(lccKey); + double areaThisTime = landUseItem.getLandCoverArea(lccKey.getFromLc()); + double areaSoFar = aggLandUse.getLandCoverArea(lccKey.getFromLc()); + double cFluxAgg = aggregateMean(cFluxSoFar, areaSoFar, cFluxThisTime, areaThisTime); + aggCFlux.setConversionCarbonFlux(lccKey, cFluxAgg); + } + + for (Map.Entry<LandCoverType, Double> cfEntry : carbonFluxItem.getEcosystemCarbonFluxMap().entrySet()) { + LandCoverType lcType = cfEntry.getKey(); + double cFluxThisTime = cfEntry.getValue(); + double cFluxSoFar = aggCFlux.getEcosystemCarbonFlux(lcType); + double areaThisTime = landUseItem.getLandCoverArea(lcType); + double areaSoFar = aggLandUse.getLandCoverArea(lcType); + double cFluxAgg = aggregateMean(cFluxSoFar, areaSoFar, cFluxThisTime, areaThisTime); + aggCFlux.setEcosystemCarbonFlux(lcType, cFluxAgg); + } + + for (Map.Entry<LandCoverType, Double> cfEntry : carbonFluxItem.getCarbonHorizonFluxMap().entrySet()) { + LandCoverType lcType = cfEntry.getKey(); + double cFluxThisTime = cfEntry.getValue(); + double cFluxSoFar = aggCFlux.getCarbonHorizonFlux(lcType); + double areaThisTime = landUseItem.getLandCoverArea(lcType); + double areaSoFar = aggLandUse.getLandCoverArea(lcType); + double cFluxAgg = aggregateMean(cFluxSoFar, areaSoFar, cFluxThisTime, areaThisTime); + aggCFlux.setCarbonHorizonFlux(lcType, cFluxAgg); + } + } + + + if (woodYieldItem != null) { + for (Map.Entry<LccKey, Double> wyEntry : woodYieldItem.getYieldMap().entrySet()) { + LccKey lccKey = wyEntry.getKey(); + double wYieldThisTime = wyEntry.getValue(); + double wYieldSoFar = aggWYield.getYieldLuc(lccKey); + double areaThisTime = landUseItem.getLandCoverArea(lccKey.getFromLc()); + double areaSoFar = aggLandUse.getLandCoverArea(lccKey.getFromLc()); + double wYieldAgg = aggregateMean(wYieldSoFar, areaSoFar, wYieldThisTime, areaThisTime); + aggWYield.setYieldLuc(lccKey, wYieldAgg); + } + + { + // Rotation wood yield + double wYieldThisTime = woodYieldItem.getYieldAtRotation(); + double wYieldSoFar = aggWYield.getYieldRota(); + double wYieldAgg = aggregateMean(wYieldSoFar, suitableAreaSoFar, wYieldThisTime, suitableAreaThisTime); + aggWYield.setYieldRota(wYieldAgg); + } + + // Rotation period + double forestRotaThisTime = woodYieldItem.getOptimalRotation(); + double forestRotaSoFar = (aggregatedForestRotation.containsKey(clusterId)) ? aggregatedForestRotation.get(clusterId) : forestRotaThisTime; + double forestRotaAgg = aggregateMean(forestRotaSoFar, suitableAreaSoFar, forestRotaThisTime, suitableAreaThisTime); + aggregatedForestRotation.put(clusterId, forestRotaAgg); + } + + // Crops yields and area fractions + if (yresp != null) { + for (CropType crop : CropType.getNonMeatTypes()) { + if (!crop.equals(CropType.SETASIDE)) { + if (irrigItem!= null) { + double irrigMax = irrigItem.getMaxIrrigAmount(crop); + + if (Double.isNaN(irrigMax)) + logWarningWithCoord("Can't find irrig max amount for ", key, yieldRaster, crop); + else + aggIrig.setMaxIrrigAmount(crop, aggregateMean(aggIrig.getMaxIrrigAmount(crop), suitableAreaSoFar, irrigMax, suitableAreaThisTime)); + } + + for (YieldType yieldType : YieldType.values()) { + double y = yresp.getYield(yieldType, crop); + if (Double.isNaN(y)) + logWarningWithCoord("Problem getting yield for type:" + yieldType + ", ", key, yieldRaster, crop); + else { + double yieldSoFar = aggYResp.getYield(yieldType, crop); + double updatedYield = aggregateMean(yieldSoFar, suitableAreaSoFar, y, suitableAreaThisTime); + aggYResp.setYield(yieldType, crop, updatedYield); + } + } + + double s = yresp.getShockRate(crop); + if (!Double.isNaN(s)) { + double shockSoFar = aggYResp.getShockRate(crop); + double updatedShock = aggregateMean(shockSoFar, suitableAreaSoFar, s, suitableAreaThisTime); + aggYResp.setShockRate(crop, updatedShock); + } + } + + double areaSoFar = aggLandUse.getCropArea(crop); + double areaThisTime = landUseItem.getCropArea(crop); + double updateCropland = (landUseItem.getLandCoverArea(LandCoverType.CROPLAND) + aggLandUse.getLandCoverArea(LandCoverType.CROPLAND)); + + if (updateCropland > 0) + aggLandUse.setCropFraction(crop, (areaSoFar + areaThisTime) / updateCropland); + + Intensity intensityThisTime = landUseItem.getIntensity(crop); + aggLandUse.setIntensity(crop, intensityThisTime); // intensity currently is always the same within a location, so can just that any. If this changes need to average here. + } + } + + // Land covers areas + for (LandCoverType lcType : LandCoverType.values()) { + double convAreaThisTime = landUseItem.getLandCoverArea(lcType, LandProtectionType.CONVERTIBLE); + double convAreaSoFar = aggLandUse.getLandCoverArea(lcType, LandProtectionType.CONVERTIBLE); + aggLandUse.setLandCoverArea(lcType, LandProtectionType.CONVERTIBLE, convAreaSoFar + convAreaThisTime); + + double protAreaThisTime = landUseItem.getLandCoverArea(lcType, LandProtectionType.PROTECTED); + double protAreaSoFar = aggLandUse.getLandCoverArea(lcType, LandProtectionType.PROTECTED); + aggLandUse.setLandCoverArea(lcType, LandProtectionType.PROTECTED, protAreaSoFar + protAreaThisTime); + } + + } + + LogWriter.println("Mapping: " + rasterInputData.getCountryInput().getCountry() + ", countFound=" + mappingsFound + ", countMissing=" + mappingsMissing); + LogWriter.println("YieldResponsesItem: " + rasterInputData.getCountryInput().getCountry() + ", countFound=" + yRespFound + ", countMissing=" + yRespMissing); + + checkedTotalAreas(landUseRaster, "before"); + checkedTotalAreas(aggregatedAreas, "after"); + + + if (!ModelConfig.IS_FORESTRY_ON) { + for (Integer locId : aggregatedYields.keySet()) { + aggregatedForestRotation.put(locId, 1.0); // shouldn't matter what this is as long as it's >0 + } + } + + // TODO shouldn't have missing data in the first place + double meanRotation = aggregatedForestRotation.values().stream().mapToDouble(o -> o.doubleValue()).sum() / aggregatedForestRotation.keySet().size(); + for (Integer locId : aggregatedYields.keySet()) { + if (!aggregatedForestRotation.containsKey(locId)) { + aggregatedForestRotation.put(locId, meanRotation); + LogWriter.printlnWarning("GamsRasterOptimister.convertFromRaster missing timber rotation; substituting average"); + } + } + + return new GamsLocationInput(rasterInputData.getTimestep(), aggregatedYields, aggregatedAreas, aggregatedIrrigCosts, + aggregatedCarbonFluxes, aggregatedWoodYields, rasterInputData.getConversionCosts(), rasterInputData.getCountryInput(), aggregatedForestRotation); + } + + private void logWarningWithCoord(String message, RasterKey key, YieldRaster yieldRaster, CropType crop) { + logWarningWithCoord(message + "crop:" + crop + ", ", key, yieldRaster); + } + + private void logWarningWithCoord(String message, RasterKey key, YieldRaster yieldRaster) { + LogWriter.printlnWarning(message + key + ", x:" + yieldRaster.getXCoordin(key) + ", y:" + yieldRaster.getYCoordin(key)); + } + + private double aggregateMean(double aggV, double aggArea, double newV, double newArea) { + if (Double.isNaN(aggV)) + return newV; + + if (newArea == 0) + return aggV; + + return (aggV*aggArea + newV*newArea) / (aggArea + newArea); + } +} diff --git a/src/ac/ed/lurg/forestry/WoodYieldItem.java b/src/ac/ed/lurg/forestry/WoodYieldItem.java index b8476d4b76f1feb607233903848764d76b679812..9027b28f303c6d95c629f4481c8490e7d737e0c5 100644 --- a/src/ac/ed/lurg/forestry/WoodYieldItem.java +++ b/src/ac/ed/lurg/forestry/WoodYieldItem.java @@ -1,111 +1,93 @@ -package ac.ed.lurg.forestry; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.Timestep; -import ac.ed.lurg.landuse.LandCoverTile; -import ac.ed.lurg.landuse.LccKey; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.LandProtectionType; -import ac.sac.raster.RasterItem; - -public class WoodYieldItem implements RasterItem { - public static final int MAX_AGE = 250; - - private Map<LccKey, Double> yield = new HashMap<LccKey, Double>(); - private int optimalRotation; // Only applies to TIMBER_FOREST - private double yieldAtRotation; // Only applies to TIMBER_FOREST - private double currentRotationHarvest; // timber harvested from current rotation - - - public WoodYieldItem() {} - - public void calcYieldData(Map<LccKey, Double[]> woodYields, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep) { - // Mean wood yield for grid cell - for (Map.Entry<LccKey, Double[]> entry : woodYields.entrySet()) { - LccKey key = entry.getKey(); - Double[] yields = entry.getValue(); - - LandCoverTile tiles = landUseTiles.get(key.getFromLc()); - - double totalArea = tiles.getTotalArea(LandProtectionType.CONVERTIBLE); // Assuming no harvest from protected areas - if (totalArea == 0) { - this.yield.put(key, 0.0) ; - } else { - double totalYield = 0; - for (int age=0; age<=LandCoverTile.getMaxAgeBin(); age++) { - int ageCapped = Math.min(age, ModelConfig.CARBON_WOOD_MAX_TIME - 1); - totalYield += yields[ageCapped] * tiles.getArea(LandProtectionType.CONVERTIBLE, age); - } - double meanYield = totalYield / totalArea; - - this.yield.put(key, meanYield); - } - } - } - - public void calcRotationData(Map<LccKey, Double[]> woodYields, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep, double woodPrice) { - // Optimal rotation - List<Double> levArr = new ArrayList<Double>(); - - Double[] yields = woodYields.get(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.TIMBER_FOREST)); // will need to update if yields no longer symmetrical - for (int age = 0; age < ModelConfig.CARBON_WOOD_MAX_TIME; age++) { - double yield = yields[age]; - double lev = (woodPrice * yield * Math.exp(-ModelConfig.DISCOUNT_RATE * age) - ModelConfig.FOREST_ESTABLISHMENT_COST) / (1 - Math.exp(-ModelConfig.DISCOUNT_RATE * age)); - levArr.add(lev); - } - - double maxLev = Collections.max(levArr); - List<Integer> candidates = new ArrayList<Integer>(); // There may be several equal maximum values - for (int i = 0; i < levArr.size(); i ++) { - if (levArr.get(i) == maxLev) { - candidates.add(i); - } - } - - optimalRotation = Collections.min(candidates); // Choose shortest rotation - yieldAtRotation = yields[optimalRotation]; - - LandCoverTile timberTiles = landUseTiles.get(LandCoverType.TIMBER_FOREST); - double timberForestArea = timberTiles.getTotalArea(LandProtectionType.CONVERTIBLE); - if (timberForestArea == 0) { - currentRotationHarvest = 0.0; - } else { - currentRotationHarvest += timberTiles.getArea(LandProtectionType.CONVERTIBLE, optimalRotation) * yieldAtRotation; - } - } - - public void setDefaultForMissingData() { - - } - - public double getYield(LandCoverType fromLc, LandCoverType toLc) { - return yield.get(new LccKey(fromLc, toLc)); - } - - public double getYield(LccKey key) { - return yield.get(key); - } - - public Map<LccKey, Double> getYieldMap() { - return yield; - } - - public int getOptimalRotation() { - return optimalRotation; - } - - public double getYieldAtRotation() { - return yieldAtRotation; - } - - public double getCurrentRotationHarvest() { - return currentRotationHarvest; - } - - -} +package ac.ed.lurg.forestry; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.Timestep; +import ac.ed.lurg.landuse.LandCoverTile; +import ac.ed.lurg.landuse.LccKey; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.sac.raster.RasterItem; + +public class WoodYieldItem implements RasterItem { + private Map<LccKey, Double> yield = new HashMap<LccKey, Double>(); + private int optimalRotation; // Only applies to TIMBER_FOREST + private double yieldAtRotation; // Only applies to TIMBER_FOREST + + public WoodYieldItem() {} + + public void calcYieldData(Map<LccKey, Double[]> woodYields, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep) { + // Mean wood yield for grid cell + for (Map.Entry<LccKey, Double[]> entry : woodYields.entrySet()) { + LccKey key = entry.getKey(); + Double[] yields = entry.getValue(); + + LandCoverTile tiles = landUseTiles.get(key.getFromLc()); + + double totalArea = tiles.getTotalArea(LandProtectionType.CONVERTIBLE); // Assuming no harvest from protected areas + if (totalArea == 0) { + this.yield.put(key, 0.0) ; + } else { + double totalYield = 0; + for (int age : tiles.getAgeKeys()) { + int ageCapped = Math.min(age, ModelConfig.CARBON_WOOD_MAX_TIME - 1); + totalYield += yields[ageCapped] * tiles.getArea(LandProtectionType.CONVERTIBLE, age); + } + double meanYield = totalYield / totalArea; + + this.yield.put(key, meanYield); + } + } + } + + public void calcRotationData(Map<LccKey, Double[]> woodYields, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep, double woodPrice) { + // Optimal rotation + List<Double> levArr = new ArrayList<Double>(); + + Double[] yields = woodYields.get(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.TIMBER_FOREST)); // will need to update if yields no longer symmetrical + for (int age = 0; age < ModelConfig.CARBON_WOOD_MAX_TIME; age++) { + double yield = yields[age]; + double lev = (woodPrice * yield * Math.exp(-ModelConfig.DISCOUNT_RATE * age) - ModelConfig.FOREST_ESTABLISHMENT_COST) / (1 - Math.exp(-ModelConfig.DISCOUNT_RATE * age)); + levArr.add(lev); + } + + double maxLev = Collections.max(levArr); + List<Integer> candidates = new ArrayList<Integer>(); // There may be several equal maximum values + for (int i = 0; i < levArr.size(); i ++) { + if (levArr.get(i) == maxLev) { + candidates.add(i); + } + } + + optimalRotation = Collections.min(candidates); // Choose shortest rotation + yieldAtRotation = yields[optimalRotation]; + } + + public void setDefaultForMissingData() { + + } + + public double getYield(LandCoverType fromLc, LandCoverType toLc) { + return yield.get(new LccKey(fromLc, toLc)); + } + + public double getYield(LccKey key) { + return yield.get(key); + } + + public Map<LccKey, Double> getYieldMap() { + return yield; + } + + public int getOptimalRotation() { + return optimalRotation; + } + + public double getYieldAtRotation() { + return yieldAtRotation; + } +} diff --git a/src/ac/ed/lurg/landuse/ConversionCostReader.java b/src/ac/ed/lurg/landuse/ConversionCostReader.java index 0a7a829ba5470b2f524ce0a18c0de93ba8d5cf32..bc87eeaffa2a7b4e5083bcd9385037548de14e14 100644 --- a/src/ac/ed/lurg/landuse/ConversionCostReader.java +++ b/src/ac/ed/lurg/landuse/ConversionCostReader.java @@ -1,85 +1,85 @@ -package ac.ed.lurg.landuse; - -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.types.LandCoverType; -import ac.ed.lurg.utils.LogWriter; - -public class ConversionCostReader { - - private static final int FROM_COL = 0; - private static final int TO_COL = 1; - private static final int COST_COL = 2; - - public Map<LccKey, Double> read() { - Map<LccKey, Double> conversionCosts = new HashMap<LccKey, Double>(); - String filename = ModelConfig.CONVERSION_COST_FILE; - try { - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line, toName, fromName; - Double cost; - 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); - - fromName = tokens[FROM_COL]; - toName = tokens[TO_COL]; - cost = Double.parseDouble(tokens[COST_COL]); - - LandCoverType fromLc = LandCoverType.getForName(fromName); - LandCoverType toLc = LandCoverType.getForName(toName); - - conversionCosts.put(new LccKey(fromLc, toLc), cost); - } - reader.close(); - - } catch (IOException e) { - LogWriter.printlnError("Failed in reading conversion costs file"); - LogWriter.print(e); - } - LogWriter.println("Processed " + filename); - - return conversionCosts; - } - - public Map<LccKey, Double> calcFromConfig() { - Map<LccKey, Double> conversionCosts = new HashMap<LccKey, Double>(); - - conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.PASTURE), ModelConfig.CROP_DECREASE_COST + ModelConfig.PASTURE_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.NATURAL), ModelConfig.CROP_DECREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.TIMBER_FOREST), ModelConfig.CROP_DECREASE_COST + ModelConfig.MANAGED_FOREST_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.CARBON_FOREST), ModelConfig.CROP_DECREASE_COST + ModelConfig.MANAGED_FOREST_INCREASE_COST); - - conversionCosts.put(new LccKey(LandCoverType.PASTURE, LandCoverType.CROPLAND), ModelConfig.PASTURE_DECREASE_COST + ModelConfig.CROP_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.PASTURE, LandCoverType.NATURAL), ModelConfig.PASTURE_DECREASE_COST); - 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.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.MANAGED_FOREST_DECREASE_COST + ModelConfig.AGRI_EXPANSION_COST_BASE_MANAGED_FOREST + ModelConfig.CROP_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.PASTURE), ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.AGRI_EXPANSION_COST_BASE_MANAGED_FOREST + 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.MANAGED_FOREST_DECREASE_COST + ModelConfig.AGRI_EXPANSION_COST_BASE_MANAGED_FOREST + ModelConfig.CROP_INCREASE_COST); - conversionCosts.put(new LccKey(LandCoverType.CARBON_FOREST, LandCoverType.PASTURE), ModelConfig.MANAGED_FOREST_DECREASE_COST + ModelConfig.AGRI_EXPANSION_COST_BASE_MANAGED_FOREST + 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); - - return conversionCosts; - } - -} +package ac.ed.lurg.landuse; + +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.types.LandCoverType; +import ac.ed.lurg.utils.LogWriter; + +public class ConversionCostReader { + + private static final int FROM_COL = 0; + private static final int TO_COL = 1; + private static final int COST_COL = 2; + + public Map<LccKey, Double> read() { + Map<LccKey, Double> conversionCosts = new HashMap<LccKey, Double>(); + String filename = ModelConfig.CONVERSION_COST_FILE; + try { + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line, toName, fromName; + Double cost; + 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); + + fromName = tokens[FROM_COL]; + toName = tokens[TO_COL]; + cost = Double.parseDouble(tokens[COST_COL]); + + LandCoverType fromLc = LandCoverType.getForName(fromName); + LandCoverType toLc = LandCoverType.getForName(toName); + + conversionCosts.put(new LccKey(fromLc, toLc), cost); + } + reader.close(); + + } catch (IOException e) { + LogWriter.printlnError("Failed in reading conversion costs file"); + LogWriter.print(e); + } + LogWriter.println("Processed " + filename); + + return conversionCosts; + } + + public Map<LccKey, Double> calcFromConfig() { + Map<LccKey, Double> conversionCosts = new HashMap<LccKey, Double>(); + + conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.PASTURE), ModelConfig.CROP_DECREASE_COST + ModelConfig.PASTURE_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.NATURAL), ModelConfig.CROP_DECREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.TIMBER_FOREST), ModelConfig.CROP_DECREASE_COST + ModelConfig.MANAGED_FOREST_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.CROPLAND, LandCoverType.CARBON_FOREST), ModelConfig.CROP_DECREASE_COST + ModelConfig.MANAGED_FOREST_INCREASE_COST); + + conversionCosts.put(new LccKey(LandCoverType.PASTURE, LandCoverType.CROPLAND), ModelConfig.PASTURE_DECREASE_COST + ModelConfig.CROP_INCREASE_COST); + conversionCosts.put(new LccKey(LandCoverType.PASTURE, LandCoverType.NATURAL), ModelConfig.PASTURE_DECREASE_COST); + 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.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.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.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); + + return conversionCosts; + } + +} diff --git a/src/ac/ed/lurg/landuse/LandCoverItem.java b/src/ac/ed/lurg/landuse/LandCoverItem.java index be2eb0b082879a3bdd397cf7094f515ac34b4668..c2733036354277d79e3071038850da8a182ece64 100644 --- a/src/ac/ed/lurg/landuse/LandCoverItem.java +++ b/src/ac/ed/lurg/landuse/LandCoverItem.java @@ -1,71 +1,199 @@ -package ac.ed.lurg.landuse; - -import java.util.HashMap; -import java.util.Map; - -import ac.ed.lurg.types.LandCoverType; -import ac.sac.raster.RasterItem; - -/** Used to hold less detailed land-cover information - * This is used in the initalisation phase, after that land-use is used */ -public class LandCoverItem implements RasterItem { - - private Map<LandCoverType, Double> landcover = new HashMap<LandCoverType, Double>(); // Land cover fraction - private Map<LandCoverType, Map<Integer, Double>> landUseAgeDist = new HashMap<LandCoverType, Map<Integer, Double>>(); - private double totalArea; - private double unavailableFract; // due to slope - private double protectedFraction; - - /** Area in Mha */ - public Double getLandCoverArea(LandCoverType landType) { - return getLandCoverFract(landType) * totalArea; - } - - public Double getLandCoverFract(LandCoverType landType) { - Double d = landcover.get(landType); - return d==null ? 0 : d.doubleValue(); - } - - public Map<LandCoverType, Double> getLandCoverMap() { - return landcover; - } - - /** Area in Mha */ - public double getTotalArea() { - return totalArea; - } - - /** Area in Mha */ - public void setTotalArea(double totalArea) { - this.totalArea = totalArea; - } - - public void setLandCoverFract(LandCoverType landType, double d) { - landcover.put(landType, d); - } - - public double getUnavailableFract(){ - return unavailableFract; - } - - public void setMaxCropFraction(double maxCropFraction){ - this.unavailableFract=1.0-maxCropFraction; - } - - public void setLandUseAgeDist(LandCoverType lcType, double prop, int ageGroup) { - Map<Integer, Double> groupMap = landUseAgeDist.computeIfAbsent(lcType, k -> new HashMap<Integer, Double>()); - groupMap.put(ageGroup, prop); - } - - public Map<LandCoverType, Map<Integer, Double>> getLandUseAgeDist() { - return landUseAgeDist; - } - - public double getProtectedFraction() { - return protectedFraction; - } - - public void setProtectedFraction(double protectedFraction) { - this.protectedFraction = protectedFraction; - } +package ac.ed.lurg.landuse; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.ed.lurg.utils.LogWriter; +import ac.sac.raster.RasterItem; + +/** Used to hold less detailed land-cover information + * This is used in the initalisation phase, after that land-use is used */ +public class LandCoverItem implements RasterItem { + + private Map<LandCoverType, Double> landCoverFract = new HashMap<LandCoverType, Double>(); // Land cover fraction + private Map<LandCoverType, Map<Integer, Double>> landUseAgeDist = new HashMap<LandCoverType, Map<Integer, Double>>(); + private Map<LandCoverType, LandCoverTile> landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); + private double totalArea; + private double unavailableFract; // due to slope + private double protectedFraction; + private List<LandCoverType> unavailPriorityList = new ArrayList<LandCoverType>( + Arrays.asList(LandCoverType.NATURAL, LandCoverType.TIMBER_FOREST, LandCoverType.PASTURE, LandCoverType.CROPLAND)); + + /** Area in Mha */ + public Double getLandCoverArea(LandCoverType landType) { + return getLandCoverFract(landType) * totalArea; + } + + public Double getLandCoverFract(LandCoverType landType) { + Double d = landCoverFract.get(landType); + return d==null ? 0 : d.doubleValue(); + } + + public Map<LandCoverType, Double> getLandCoverMap() { + return landCoverFract; + } + + /** Area in Mha */ + public double getTotalArea() { + return totalArea; + } + + /** Area in Mha */ + public void setTotalArea(double totalArea) { + this.totalArea = totalArea; + } + + public void setLandCoverFract(LandCoverType landType, double d) { + landCoverFract.put(landType, d); + } + + public double getUnavailableFract(){ + return unavailableFract; + } + + public void setMaxCropFraction(double maxCropFraction){ + this.unavailableFract=1.0-maxCropFraction; + } + + public void setLandUseAgeDist(LandCoverType lcType, double prop, int ageGroup) { + Map<Integer, Double> groupMap = landUseAgeDist.computeIfAbsent(lcType, k -> new HashMap<Integer, Double>()); + groupMap.put(ageGroup, prop); + } + + public Map<LandCoverType, Map<Integer, Double>> getLandUseAgeDist() { + return landUseAgeDist; + } + + public double getProtectedFraction() { + return protectedFraction; + } + + public void setProtectedFraction(double protectedFraction) { + this.protectedFraction = protectedFraction; + } + + public Map<LandCoverType, LandCoverTile> getLandCoverTiles() { + createLandCoverTiles(); + setUnavailableArea(); + return landCoverAreas; + } + + public void createLandCoverTiles() { + + for (LandCoverType lcType : LandCoverType.values()) { + landCoverAreas.put(lcType, new LandCoverTile()); + } + + double protectableFraction = 0; + for (LandCoverType lcType : LandCoverType.getProtectibleTypes()) { + protectableFraction += getLandCoverFract(lcType); + } + // need to scale protectedFraction by protectableFraction and cap at 1.0. Gives fraction of protectable LC types to protect + double adjProtectedFraction = Math.min(1.0, protectedFraction / protectableFraction); + + for (LandCoverType lcType : LandCoverType.values()) { + LandCoverTile tiles = landCoverAreas.get(lcType); + double totalArea = getLandCoverArea(lcType); + if (totalArea == 0) + continue; + + // static land covers + if (!lcType.isConvertible()) { + tiles.addArea(LandProtectionType.UNAVAILABLE, 0, totalArea); // Assumes land cover age is 0 + continue; + } + // convertible land covers + double protectedArea; + double unprotectedArea; + if (lcType.isProtectable()) { + protectedArea = totalArea * adjProtectedFraction; + unprotectedArea = totalArea * (1 - adjProtectedFraction); + } else { + protectedArea = 0; + unprotectedArea = totalArea; + } + + Map<Integer, Double> ageMap = getLandUseAgeDist().get(lcType); + double totalProp = ageMap.values().stream().reduce(0.0, Double::sum); + if (totalProp == 0.0) { // If no distribution given then assume land is in oldest age group + int maxAgeGroup = ageMap.keySet().stream().max(Integer::compare).get(); + ageMap.put(maxAgeGroup, 1.0); + } + if (totalProp > 1.0) { + // normalise + for (Map.Entry<Integer, Double> entry : ageMap.entrySet()) { + ageMap.put(entry.getKey(), entry.getValue() / totalProp); + } + } + + // Assuming land cover area all in middle of age group + for (Map.Entry<Integer, Double> ageEntry : ageMap.entrySet()) { + double prop = ageEntry.getValue(); + if (prop == 0.0) + continue; + int ageGroup = ageEntry.getKey(); + int minAge = (int) (ageGroup - 1) * ModelConfig.LAND_COVER_INIT_AGE_GROUP_SIZE + 1; + int midAge = minAge + ModelConfig.LAND_COVER_INIT_AGE_GROUP_SIZE / 2; + + landCoverAreas.get(lcType).setArea(LandProtectionType.CONVERTIBLE, midAge, unprotectedArea * prop); + landCoverAreas.get(lcType).setArea(LandProtectionType.PROTECTED, midAge, protectedArea * prop); + } + } + + // Check that areas have been allocated correctly + Map<LandCoverType, Double> initAreas = new HashMap<LandCoverType, Double>(); + for (LandCoverType lc : LandCoverType.values()) { + initAreas.put(lc, getLandCoverArea(lc)); + } + + runAreaCheck(initAreas, landCoverAreas); + } + + private void setUnavailableArea() { + double alreadyUnavail = landCoverAreas.values().stream().mapToDouble(o -> o.getTotalArea(LandProtectionType.UNAVAILABLE)).sum(); + double unavailableArea = unavailableFract * totalArea; + double shortfall = Math.max(0, unavailableArea - alreadyUnavail); + if (shortfall == 0) + return; + + for (LandCoverType lcType : unavailPriorityList) { + double cArea = landCoverAreas.get(lcType).getTotalArea(LandProtectionType.CONVERTIBLE); + double pArea = landCoverAreas.get(lcType).getTotalArea(LandProtectionType.PROTECTED); + double totalArea = cArea + pArea; + double cProp = cArea / totalArea; + + double additionalUnavail = Math.min(shortfall, totalArea); + if (additionalUnavail > 0) { + landCoverAreas.get(lcType).addArea(LandProtectionType.UNAVAILABLE, 0, additionalUnavail); + landCoverAreas.get(lcType).removeArea(LandProtectionType.CONVERTIBLE, additionalUnavail * cProp); + landCoverAreas.get(lcType).removeArea(LandProtectionType.PROTECTED, additionalUnavail * (1 - cProp)); + shortfall -= additionalUnavail; + } + + if (shortfall == 0) + break; + } + + if (shortfall > 0) { + LogWriter.printlnWarning("LandUseItem.setUnavailableArea insufficient area to make unavailable"); + } + } + + public void runAreaCheck(Map<LandCoverType, Double> lcAreas, Map<LandCoverType, LandCoverTile> landCoverAreas) { + final double THRESHOLD = 1e-7; + for (LandCoverType lcType : landCoverAreas.keySet()) { + double initArea = lcAreas.get(lcType); + double processedArea = landCoverAreas.get(lcType).getTotalArea(); + if (Math.abs(initArea - processedArea) > THRESHOLD) { + LogWriter.printlnError("LandUseItem: processed area and initial area different: " + lcType.getName() + " " + processedArea + ", " + initArea); + } + } + } + } \ No newline at end of file diff --git a/src/ac/ed/lurg/landuse/LandCoverTile.java b/src/ac/ed/lurg/landuse/LandCoverTile.java index ecc8d4c077752ad978be8aa4eb87563be8fcf098..776add086d3443d9de4ec61c5b3cc2f54cb2d249 100644 --- a/src/ac/ed/lurg/landuse/LandCoverTile.java +++ b/src/ac/ed/lurg/landuse/LandCoverTile.java @@ -1,113 +1,187 @@ -package ac.ed.lurg.landuse; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.utils.LogWriter; -import ac.sac.raster.InterpolatingRasterItem; -import ac.ed.lurg.types.LandProtectionType; - -public class LandCoverTile implements Serializable, InterpolatingRasterItem<LandCoverTile> { - private static final long serialVersionUID = 3208516181615267472L; - private static final int MAX_AGE = 250; - - private Map<LandProtectionType, LandParcel> areas; - - public LandCoverTile() { - this.areas = new HashMap<LandProtectionType, LandParcel>(); - for (LandProtectionType lpType : LandProtectionType.values()) { - areas.put(lpType, new LandParcel()); - } - } - - public double getArea(LandProtectionType lpType, int age) { - return areas.get(lpType).get(age); - } - - public void addArea(LandProtectionType lpType, double a) { - areas.get(lpType).add(0, a); // new area is always age=0 - } - - public void addArea(LandProtectionType lpType, int age, double a) { - areas.get(lpType).add(age, a); - } - - public void setArea(LandProtectionType lpType, int age, double a) { - areas.get(lpType).set(age, a); - } - - public double getTotalArea(LandProtectionType lpType) { - return areas.get(lpType).getTotal(); - } - - public void removeArea(LandProtectionType lpType, double a) { // Subtracts area uniformly - areas.get(lpType).subtractUniform(a); - } - - public void removeArea(LandProtectionType lpType, int age, double a) { - areas.get(lpType).subtract(age, a); - } - - public double getTotalArea() { - double area = 0; - for (LandProtectionType lpType : LandProtectionType.values()) { - area += getTotalArea(lpType); - } - return area; - } - - public void resetAreaForAge(LandProtectionType lpType, int age) { - areas.get(lpType).resetAge(age); - } - - public void resetAreaForAge(LandProtectionType lpType, int age, double area) { - if (area > getArea(lpType, age)) { - LogWriter.printlnError("LandCoverTile.resetAreaForAge: not enough area"); - return; - } - addArea(lpType, area); - removeArea(lpType, age, area); - } - - public void removeAllArea() { - for (LandProtectionType lpType : LandProtectionType.values()) { - areas.get(lpType).clearAll(); - } - } - - public void incrementAge() { - for (int t = 0; t < ModelConfig.TIMESTEP_SIZE; t++) { - for (LandProtectionType lpType : LandProtectionType.values()) { - areas.get(lpType).incrementAge(); - } - } - } - - // Moves area from convertible to protected, maintaining age classes - public void moveAreaToProtected(double area) { - double convertibleA = getTotalArea(LandProtectionType.CONVERTIBLE); - if (area > convertibleA) { - LogWriter.printlnWarning("LandCoverTile.moveAreaToProtected cannot protect all required area"); - area = Math.min(convertibleA, area); - } - double fractToMove = area / convertibleA; - for (int i = 0; i <= MAX_AGE; i ++) { - double areaToMove = getArea(LandProtectionType.CONVERTIBLE, i) * fractToMove; - addArea(LandProtectionType.PROTECTED, i, areaToMove); - removeArea(LandProtectionType.CONVERTIBLE, i, areaToMove); - } - } - - public static int getMaxAgeBin() { - return MAX_AGE; - } - - @Override - public void interpolateAll(LandCoverTile fromItem, LandCoverTile toItem, double factor) { - // TODO Auto-generated method stub - - } - -} +package ac.ed.lurg.landuse; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.utils.LogWriter; +import ac.sac.raster.InterpolatingRasterItem; +import ac.ed.lurg.types.LandProtectionType; + +public class LandCoverTile implements Serializable, InterpolatingRasterItem<LandCoverTile> { + private static final long serialVersionUID = 3208516181615267472L; + + private Map<LandKey, Double> areas = new HashMap<LandKey, Double>(); + + public LandCoverTile() {} + + public double getArea(LandProtectionType lpType, int age) { + LandKey key = new LandKey(lpType, age); + return getArea(key); + } + + public double getArea(LandKey key) { + return areas.getOrDefault(key, 0.0); + } + + public void setArea(LandProtectionType lpType, int age, double a) { + LandKey key = new LandKey(lpType, age); + setArea(key, a); + } + + public void setArea(LandKey key, double a) { + areas.put(key, a); + } + + public void addArea(LandProtectionType lpType, int age, double a) { + LandKey key = new LandKey(lpType, age); + double prevArea = getArea(key); + areas.put(key, prevArea + a); + } + + public void addArea(LandProtectionType lpType, double a) { + double totalArea = getTotalArea(lpType); + if (totalArea == 0) { + for (int age = 0; age <= 150; age+=10) { + addArea(lpType, age, a/16); // assuming even distribution + } + + } else { + double factor = (a + totalArea) / totalArea; + for (LandKey key : areas.keySet()) { + if (key.getLpType().equals(lpType)) { + double prevArea = getArea(key); + setArea(key, prevArea * factor); + } + } + } + } + + public double getTotalArea(LandProtectionType lpType) { + double a = areas.keySet() + .stream() + .filter(k -> k.getLpType().equals(lpType)) + .mapToDouble(k -> areas.get(k)) + .sum(); + return a; + } + + public void removeArea(LandProtectionType lpType, int age, double a) { + if (a == 0) + return; + LandKey key = new LandKey(lpType, age); + double prevArea = getArea(key); + if (a > prevArea) { + LogWriter.printlnError("LandCoverTile: attempting to remove too much area."); + a = Math.min(a, prevArea); + } + setArea(key, prevArea - a); + } + + public void removeArea(LandProtectionType lpType, double a) { // Subtracts area uniformly + if (a == 0) + return; + double totalArea = getTotalArea(lpType); + double areaToRemove = Math.min(a, totalArea); + if (Math.abs(a - areaToRemove) > 1e-10) { + LogWriter.printlnError("LandCoverTile: attempting to remove too much area."); + } + double factor = (totalArea - areaToRemove) / totalArea; + for (LandKey key : areas.keySet()) { + if (key.getLpType().equals(lpType)) { + double prevArea = getArea(key); + setArea(key, prevArea * factor); + } + } + } + + public double getTotalArea() { + double area = 0; + for (LandProtectionType lpType : LandProtectionType.values()) { + area += getTotalArea(lpType); + } + return area; + } + + public void resetAreaForAge(LandProtectionType lpType, int age) { + LandKey key = new LandKey(lpType, age); + double a = getArea(key); + areas.remove(key); + setArea(lpType, 0, a); + } + + public void resetAreaForAge(LandProtectionType lpType, int age, double a) { + a = Math.min(getArea(lpType, age), a); + removeArea(lpType, age, a); + addArea(lpType, 0, a); + } + + public void removeAllArea(LandProtectionType lpType) { + Map<LandKey, Double> newAreas = new HashMap<LandKey, Double>(); + for (LandKey key : areas.keySet()) { + if (!key.getLpType().equals(lpType)) { + newAreas.put(key, getArea(key));; + } + } + areas.clear(); + areas.putAll(newAreas); + } + + public void incrementAge() { + Map<LandKey, Double> newAreas = new HashMap<LandKey, Double>(); + for (LandKey key : areas.keySet()) { + LandKey newKey = new LandKey(key.getLpType(), key.getAge() + ModelConfig.TIMESTEP_SIZE); + newAreas.put(newKey, getArea(key)); + } + areas.clear(); + areas.putAll(newAreas); + } + + // Moves area between land protection types + public void moveArea(LandProtectionType fromPt, LandProtectionType toPt, double area) { + double fromA = getTotalArea(fromPt); + if (area > fromA) { + LogWriter.printlnWarning("LandCoverTile.moveAreaToProtected cannot protect all required area"); + area = Math.min(fromA, area); + } + double fractToMove = area / fromA; + for (LandKey key : areas.keySet()) { + if (key.getLpType().equals(fromPt)) { + double areaToMove = getArea(key) * fractToMove; + addArea(toPt, key.getAge(), areaToMove); + removeArea(fromPt, key.getAge(), areaToMove); + } + } + } + + public Set<Integer> getAgeKeys() { // TODO can be more efficient + Set<Integer> ageKeys = new HashSet<Integer>(); + for (LandKey key : areas.keySet()) { + ageKeys.add(key.getAge()); + } + return ageKeys; + } + + // remove 0 areas + public void cleanUp() { + Map<LandKey, Double> newAreas = new HashMap<LandKey, Double>(); + for (LandKey key : areas.keySet()) { + double a = getArea(key); + if (a > 0) { + newAreas.put(key, a); + } + } + areas.clear(); + areas.putAll(newAreas); + } + + @Override + public void interpolateAll(LandCoverTile fromItem, LandCoverTile toItem, double factor) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/ac/ed/lurg/landuse/LandKey.java b/src/ac/ed/lurg/landuse/LandKey.java new file mode 100644 index 0000000000000000000000000000000000000000..aae009aea20747fc517b8101cbe78c97f5cafa7d --- /dev/null +++ b/src/ac/ed/lurg/landuse/LandKey.java @@ -0,0 +1,45 @@ +package ac.ed.lurg.landuse; + +import java.io.Serializable; +import java.util.Objects; + +import ac.ed.lurg.types.LandProtectionType; + +public class LandKey implements Serializable { + private static final long serialVersionUID = -1173322425708587699L; + + private LandProtectionType lpType; + private int age; // years since conversion + + public LandKey(LandProtectionType lpType, int age) { + super(); + this.lpType = lpType; + this.age = age; + } + + public LandProtectionType getLpType() { + return lpType; + } + + public int getAge() { + return age; + } + + @Override + public int hashCode() { + return Objects.hash(age, lpType); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LandKey other = (LandKey) obj; + return age == other.age && lpType == other.lpType; + } + +} diff --git a/src/ac/ed/lurg/landuse/LandParcel.java b/src/ac/ed/lurg/landuse/LandParcel.java deleted file mode 100644 index 7dc3aa643328e5ddd8808f64ab2b495c5c7851c2..0000000000000000000000000000000000000000 --- a/src/ac/ed/lurg/landuse/LandParcel.java +++ /dev/null @@ -1,89 +0,0 @@ -package ac.ed.lurg.landuse; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -import ac.ed.lurg.utils.LogWriter; - -public class LandParcel implements Serializable { - private static final long serialVersionUID = -114360206977984739L; - Map<Integer, Double> parcels; - - public LandParcel() { - this.parcels = new TreeMap<Integer, Double>(); - } - - public void set(int age, double area) { - parcels.put(age, area); - } - - public double get(int age) { - return parcels.getOrDefault(age, 0.0); - } - - public void add(int age, double area) { - double newArea = get(age) + area; - set(age, newArea); - } - - public void subtract(int age, double area) { - double prevArea = get(age); - double newArea; - if (prevArea < area) { - LogWriter.printlnError("LandParcel: Attempting to subtract too much area"); - newArea = 0.0; - } else { - newArea = prevArea - area; - } - set(age, newArea); - } - - public void subtractUniform(double area) { - if (area <= 0) - return; - double totalArea = getTotal(); - double areaToRemove = Math.min(totalArea, area); - if (Math.abs(area - areaToRemove) > 1e-10) { - LogWriter.printlnError("LandParcel.subtractUniform: attempting to remove too much area: " + area + ", available: " + totalArea); - } - double factor = (totalArea - areaToRemove) / totalArea; - for (int i : parcels.keySet()) { - double newArea = get(i) * factor; - set(i, newArea); - } - } - - public double getTotal() { - return parcels.values().stream().reduce(0.0, Double::sum); - } - - public void resetAge(int age) { - double area = get(age); - set(age, 0.0); - set(0, area); - } - - public void incrementAge() { - if (parcels.size() == 0) - return; - - Set<Integer> keys = parcels.keySet(); - List<Integer> orderedKeys = new ArrayList<Integer>(keys); - Collections.sort(orderedKeys); - Collections.reverse(orderedKeys); - for (int age : orderedKeys) { - double area = get(age); - set(age + 1, area); - parcels.remove(age); - } - } - - public void clearAll() { - parcels.clear(); - } -} diff --git a/src/ac/ed/lurg/landuse/LandTileReader.java b/src/ac/ed/lurg/landuse/LandTileReader.java index d36ff6911dd547ae9256b3e743eeb18e5d240344..4a3b3aae412bd56ee8917d5aedb41a622419026a 100644 --- a/src/ac/ed/lurg/landuse/LandTileReader.java +++ b/src/ac/ed/lurg/landuse/LandTileReader.java @@ -1,26 +1,26 @@ -package ac.ed.lurg.landuse; - -import java.util.Map; - -import ac.ed.lurg.types.LandCoverType; -import ac.sac.raster.AbstractTabularRasterReader; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -public class LandTileReader extends AbstractTabularRasterReader<LandCoverItem> { - private static final int MIN_COLS = 6; - - public LandTileReader(RasterSet<LandCoverItem> data) { - super("[ |\t]+", MIN_COLS, data); - } - - @Override - protected void setData(RasterKey key, LandCoverItem item, Map<String, Double> rowValues) { - int ageGroup = (int) getValueForCol(rowValues, "AgeGroup"); - item.setLandUseAgeDist(LandCoverType.CROPLAND, getValueForCol(rowValues, "CROPLAND"), ageGroup); - item.setLandUseAgeDist(LandCoverType.PASTURE, getValueForCol(rowValues, "PASTURE"), ageGroup); - item.setLandUseAgeDist(LandCoverType.TIMBER_FOREST, getValueForCol(rowValues, "FOREST"), ageGroup); - item.setLandUseAgeDist(LandCoverType.CARBON_FOREST, getValueForCol(rowValues, "FOREST"), ageGroup); - item.setLandUseAgeDist(LandCoverType.NATURAL, getValueForCol(rowValues, "FOREST"), ageGroup); - } -} +package ac.ed.lurg.landuse; + +import java.util.Map; + +import ac.ed.lurg.types.LandCoverType; +import ac.sac.raster.AbstractTabularRasterReader; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterSet; + +public class LandTileReader extends AbstractTabularRasterReader<LandCoverItem> { + private static final int MIN_COLS = 6; + + public LandTileReader(RasterSet<LandCoverItem> data) { + super("[ |\t]+", MIN_COLS, data); + } + + @Override + protected void setData(RasterKey key, LandCoverItem item, Map<String, Double> rowValues) { + int ageGroup = (int) getValueForCol(rowValues, "AgeGroup"); + item.setLandUseAgeDist(LandCoverType.CROPLAND, getValueForCol(rowValues, "CROPLAND"), ageGroup); + item.setLandUseAgeDist(LandCoverType.PASTURE, getValueForCol(rowValues, "PASTURE"), ageGroup); + item.setLandUseAgeDist(LandCoverType.TIMBER_FOREST, getValueForCol(rowValues, "FOREST"), ageGroup); + item.setLandUseAgeDist(LandCoverType.CARBON_FOREST, getValueForCol(rowValues, "FOREST"), ageGroup); + item.setLandUseAgeDist(LandCoverType.NATURAL, getValueForCol(rowValues, "FOREST"), ageGroup); + } +} diff --git a/src/ac/ed/lurg/landuse/LandUseBinarySerializer.java b/src/ac/ed/lurg/landuse/LandUseBinarySerializer.java index 832afbc4be74b15c8bef0327c4a3317d16c28e4f..320441b88226bf90cb3834985ba396998e5a4072 100644 --- a/src/ac/ed/lurg/landuse/LandUseBinarySerializer.java +++ b/src/ac/ed/lurg/landuse/LandUseBinarySerializer.java @@ -1,194 +1,186 @@ -package ac.ed.lurg.landuse; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.LandProtectionType; -import ac.ed.lurg.utils.LogWriter; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -public class LandUseBinarySerializer { - private static final int NUM_PROTECTION_CLASSES = 3; // convertible, protected, unavailable - private static final int NUM_AGE_CLASSES = LandCoverTile.getMaxAgeBin() + 1; - private static final int CONVERTIBLE_IDX = 0; - private static final int PROTECTED_IDX = 1; - private static final int UNVAVAILABLE_IDX = 2; - - public LandUseBinarySerializer() {} - - public void serializeLandUse(RasterSet<LandUseItem> luRaster) { - long startTime = System.currentTimeMillis(); - - FileOutputStream outputStream = null; - DataOutputStream writer = null; - - try { - outputStream = new FileOutputStream(ModelConfig.SERIALIZED_LAND_COVER_FILE); - writer = new DataOutputStream(new BufferedOutputStream(outputStream)); - - // TODO file header and checks: num lc types, order, num protection classes, vector size - int count =0; - for (Map.Entry<RasterKey, LandUseItem> luEntry : luRaster.entrySet()) { - count++; - RasterKey key = luEntry.getKey(); - LandUseItem luItem = luEntry.getValue(); - - // Write coordinates - writer.writeDouble(key.getCol()); - writer.writeDouble(key.getRow()); - - Map<LandCoverType, LandCoverTile> tiles = luItem.getLandCoverTiles(); - - // Convertible - for (LandCoverType lcType : LandCoverType.values()) { - LandCoverTile tile = tiles.get(lcType); - for (int a = 0; a < NUM_AGE_CLASSES; a++) { - writer.writeDouble(tile.getArea(LandProtectionType.CONVERTIBLE, a)); - } - } - - // Protected - for (LandCoverType lcType : LandCoverType.values()) { - LandCoverTile tile = tiles.get(lcType); - for (int a = 0; a < NUM_AGE_CLASSES; a++) { - writer.writeDouble(tile.getArea(LandProtectionType.PROTECTED, a)); - } - } - - // Unavailable - for (LandCoverType lcType : LandCoverType.values()) { - LandCoverTile tile = tiles.get(lcType); - writer.writeDouble(tile.getTotalArea(LandProtectionType.UNAVAILABLE)); - } - //LogWriter.println(""+count); - } - writer.close(); - - - } - catch (Exception e) { - LogWriter.printlnError("Problem reading data file " + ModelConfig.SERIALIZED_LAND_COVER_FILE); - LogWriter.print(e); - throw new RuntimeException(e); - } - finally { - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - } - LogWriter.println("Serialized land cover " + ModelConfig.SERIALIZED_LAND_COVER_FILE + ", took " + (System.currentTimeMillis() - startTime) + " ms"); - } - - public void deserializeLandUse(RasterSet<LandUseItem> luRaster) { - long startTime = System.currentTimeMillis(); - - FileInputStream inputStream = null; - DataInputStream reader = null; - - try { - inputStream = new FileInputStream(ModelConfig.SERIALIZED_LAND_COVER_FILE); - BufferedInputStream buffInputStream = new BufferedInputStream(inputStream); - buffInputStream.mark(8); - reader = new DataInputStream(buffInputStream); - - int count=0; - - while(reader.available() > 0) { - // Coordinates loop - - int col = (int) reader.readDouble(); - int row = (int) reader.readDouble(); - - Map<LandCoverType, LandCoverTile> landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); - for (LandCoverType lcType: LandCoverType.values()) { - landCoverAreas.put(lcType, new LandCoverTile()); - } - - LandUseItem luItem = luRaster.get(new RasterKey(col, row)); - - // Protection class loop - for (int protIdx = 0; protIdx < NUM_PROTECTION_CLASSES; protIdx++) { - - switch(protIdx) { - case CONVERTIBLE_IDX: - for (LandCoverType lcType : LandCoverType.values()) { - for (int age = 0; age < NUM_AGE_CLASSES; age++) { - double area = reader.readDouble(); - if (area > 0) - landCoverAreas.get(lcType).setArea(LandProtectionType.CONVERTIBLE, age, area); - } - } - break; - case PROTECTED_IDX: - for (LandCoverType lcType : LandCoverType.values()) { - for (int age = 0; age < NUM_AGE_CLASSES; age++) { - double area = reader.readDouble(); - if (area > 0) - landCoverAreas.get(lcType).setArea(LandProtectionType.PROTECTED, age, area); - } - } - break; - case UNVAVAILABLE_IDX: - for (LandCoverType lcType : LandCoverType.values()) { - double area = reader.readDouble(); - if (area > 0) - landCoverAreas.get(lcType).setArea(LandProtectionType.UNAVAILABLE, 0, area); // TODO Do we need to keep track of age? - } - } - } - - luItem.overwriteLandCoverAreas(landCoverAreas); - count++; - /*if(Math.floorMod(count, 1000)==0) { - LogWriter.println(""+count); - }*/ - } - - reader.close(); - } - catch (Exception e) { - LogWriter.printlnError("Problem reading data file " + ModelConfig.SERIALIZED_LAND_COVER_FILE); - LogWriter.print(e); - throw new RuntimeException(e); - } - finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - LogWriter.print(e); - } - } - } - LogWriter.println("Deserialized land cover " + ModelConfig.SERIALIZED_LAND_COVER_FILE + ", took " + (System.currentTimeMillis() - startTime) + " ms"); - } - -} +package ac.ed.lurg.landuse; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.ed.lurg.utils.LogWriter; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterSet; + +public class LandUseBinarySerializer { + private static final int NUM_PROTECTION_CLASSES = 3; // convertible, protected, unavailable +// private static final int NUM_AGE_CLASSES = LandCoverTile.getMaxAgeBin() + 1; + private static final int NUM_AGE_CLASSES = 250 + 1; + private static final int CONVERTIBLE_IDX = 0; + private static final int PROTECTED_IDX = 1; + private static final int UNVAVAILABLE_IDX = 2; + + public LandUseBinarySerializer() {} + + public void serializeLandUse(RasterSet<LandUseItem> luRaster) { + long startTime = System.currentTimeMillis(); + + FileOutputStream outputStream = null; + DataOutputStream writer = null; + + try { + outputStream = new FileOutputStream(ModelConfig.SERIALIZED_LAND_COVER_FILE); + writer = new DataOutputStream(new BufferedOutputStream(outputStream)); + + // TODO file header and checks: num lc types, order, num protection classes, vector size + for (Map.Entry<RasterKey, LandUseItem> luEntry : luRaster.entrySet()) { + RasterKey key = luEntry.getKey(); + LandUseItem luItem = luEntry.getValue(); + + // Write coordinates + writer.writeDouble(key.getCol()); + writer.writeDouble(key.getRow()); + + Map<LandCoverType, LandCoverTile> tiles = luItem.getLandCoverTiles(); + + // Convertible + for (LandCoverType lcType : LandCoverType.values()) { + LandCoverTile tile = tiles.get(lcType); + for (int a = 0; a < NUM_AGE_CLASSES; a++) { + writer.writeDouble(tile.getArea(LandProtectionType.CONVERTIBLE, a)); + } + } + + // Protected + for (LandCoverType lcType : LandCoverType.values()) { + LandCoverTile tile = tiles.get(lcType); + for (int a = 0; a < NUM_AGE_CLASSES; a++) { + writer.writeDouble(tile.getArea(LandProtectionType.PROTECTED, a)); + } + } + + // Unavailable + for (LandCoverType lcType : LandCoverType.values()) { + LandCoverTile tile = tiles.get(lcType); + writer.writeDouble(tile.getTotalArea(LandProtectionType.UNAVAILABLE)); + } + } + writer.close(); + + + } + catch (Exception e) { + LogWriter.printlnError("Problem reading data file " + ModelConfig.SERIALIZED_LAND_COVER_FILE); + LogWriter.print(e); + throw new RuntimeException(e); + } + finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + } + LogWriter.println("Serialized land cover " + ModelConfig.SERIALIZED_LAND_COVER_FILE + ", took " + (System.currentTimeMillis() - startTime) + " ms"); + } + + public void deserializeLandUse(RasterSet<LandUseItem> luRaster) { + long startTime = System.currentTimeMillis(); + + FileInputStream inputStream = null; + DataInputStream reader = null; + + try { + inputStream = new FileInputStream(ModelConfig.SERIALIZED_LAND_COVER_FILE); + BufferedInputStream buffInputStream = new BufferedInputStream(inputStream); + buffInputStream.mark(8); + reader = new DataInputStream(buffInputStream); + + while(reader.available() > 0) { + // Coordinates loop + + int col = (int) reader.readDouble(); + int row = (int) reader.readDouble(); + + Map<LandCoverType, LandCoverTile> landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); + for (LandCoverType lcType: LandCoverType.values()) { + landCoverAreas.put(lcType, new LandCoverTile()); + } + + LandUseItem luItem = luRaster.get(new RasterKey(col, row)); + + // Protection class loop + for (int protIdx = 0; protIdx < NUM_PROTECTION_CLASSES; protIdx++) { + + switch(protIdx) { + case CONVERTIBLE_IDX: + for (LandCoverType lcType : LandCoverType.values()) { + for (int age = 0; age < NUM_AGE_CLASSES; age++) { + double area = reader.readDouble(); + if (area > 0) + landCoverAreas.get(lcType).setArea(LandProtectionType.CONVERTIBLE, age, area); + } + } + break; + case PROTECTED_IDX: + for (LandCoverType lcType : LandCoverType.values()) { + for (int age = 0; age < NUM_AGE_CLASSES; age++) { + double area = reader.readDouble(); + if (area > 0) + landCoverAreas.get(lcType).setArea(LandProtectionType.PROTECTED, age, area); + } + } + break; + case UNVAVAILABLE_IDX: + for (LandCoverType lcType : LandCoverType.values()) { + double area = reader.readDouble(); + if (area > 0) + landCoverAreas.get(lcType).setArea(LandProtectionType.UNAVAILABLE, 0, area); // TODO Do we need to keep track of age? + } + } + } + + luItem.overwriteLandCoverAreas(landCoverAreas); + } + + reader.close(); + } + catch (Exception e) { + LogWriter.printlnError("Problem reading data file " + ModelConfig.SERIALIZED_LAND_COVER_FILE); + LogWriter.print(e); + throw new RuntimeException(e); + } + finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + LogWriter.print(e); + } + } + } + LogWriter.println("Deserialized land cover " + ModelConfig.SERIALIZED_LAND_COVER_FILE + ", took " + (System.currentTimeMillis() - startTime) + " ms"); + } + +} diff --git a/src/ac/ed/lurg/landuse/LandUseItem.java b/src/ac/ed/lurg/landuse/LandUseItem.java index c96f78659628c8e9e5e6414849904bb0f1c6185c..8385ddf874673efe33d23b8e337b656e074eb474 100644 --- a/src/ac/ed/lurg/landuse/LandUseItem.java +++ b/src/ac/ed/lurg/landuse/LandUseItem.java @@ -1,602 +1,461 @@ -package ac.ed.lurg.landuse; - -import java.io.Serializable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.types.CropToDouble; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.LandProtectionType; -import ac.ed.lurg.utils.Interpolator; -import ac.ed.lurg.utils.LogWriter; -import ac.sac.raster.InterpolatingRasterItem; - -public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serializable { - private static final long serialVersionUID = 763526147224355158L; - - private Map<CropType, Intensity> intensityMap = new HashMap<CropType, Intensity>(); - private Map<CropType, Double> cropFractions = new HashMap<CropType, Double>(); - private Map<LandCoverType, LandCoverTile> landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); - private double protectedFraction; // protected fraction of total area - - public LandUseItem() { - for (LandCoverType lcType : LandCoverType.values()) { - landCoverAreas.put(lcType, new LandCoverTile()); - } - } - - public LandUseItem(LandCoverItem landCover) { - this(); - if (landCover != null) { - setCropFraction(CropType.WHEAT, 0.5); // random start as don't have better data - setCropFraction(CropType.MAIZE, 0.5); - setProtectedFraction(landCover.getProtectedFraction()); - addLandCoverTiles(landCover); - setUnavailableArea(landCover.getUnavailableFract()); - - } - } - - public LandUseItem(LandUseItem luItemToCopy) { - this(); - intensityMap.putAll(luItemToCopy.intensityMap); - cropFractions.putAll(luItemToCopy.cropFractions); - landCoverAreas.putAll(luItemToCopy.landCoverAreas); - protectedFraction = (luItemToCopy.protectedFraction); - } - - public void overwriteLandCoverAreas(Map<LandCoverType, LandCoverTile> landCoverAreas) { - this.landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); - this.landCoverAreas.putAll(landCoverAreas); - } - - public void addLandCoverTiles(LandCoverItem landCover) { - double protectableFraction = 0; - for (LandCoverType lcType : LandCoverType.getProtectibleTypes()) { - protectableFraction += landCover.getLandCoverFract(lcType); - } - // need to scale protectedFraction by protectableFraction and cap at 1.0. Gives fraction of protectable LC types to protect - double adjProtectedFraction = Math.min(1.0, protectedFraction / protectableFraction); - - for (LandCoverType lcType : LandCoverType.values()) { - LandCoverTile tiles = landCoverAreas.get(lcType); - double totalArea = landCover.getLandCoverArea(lcType); - if (totalArea == 0) - continue; - - // static land covers - if (!lcType.isConvertible()) { - tiles.addArea(LandProtectionType.UNAVAILABLE, totalArea); // Assumes land cover age is 0 - continue; - } - // convertible land covers - double protectedArea; - double unprotectedArea; - if (lcType.isProtectable()) { - protectedArea = totalArea * adjProtectedFraction; - unprotectedArea = totalArea * (1 - adjProtectedFraction); - } else { - protectedArea = 0; - unprotectedArea = totalArea; - } - - Map<Integer, Double> ageMap = landCover.getLandUseAgeDist().get(lcType); - double totalProp = ageMap.values().stream().reduce(0.0, Double::sum); - if (totalProp == 0.0) { // If no distribution given then assume land is in oldest age group - int maxAgeGroup = ageMap.keySet().stream().max(Integer::compare).get(); - ageMap.put(maxAgeGroup, 1.0); - } - if (totalProp > 1.0) { - // normalise - for (Map.Entry<Integer, Double> entry : ageMap.entrySet()) { - ageMap.put(entry.getKey(), entry.getValue() / totalProp); - } - } - - final int AGE_GROUP_SIZE = 10; - // Assuming land cover area all in middle age group - for (Map.Entry<Integer, Double> ageEntry : ageMap.entrySet()) { - double prop = ageEntry.getValue(); - if (prop == 0.0) - continue; - int ageGroup = ageEntry.getKey(); - int minAge = (int) (ageGroup - 1) * AGE_GROUP_SIZE + 1; - int maxAge = minAge + (AGE_GROUP_SIZE - 1); - int midAge = minAge + AGE_GROUP_SIZE / 2; - - landCoverAreas.get(lcType).setArea(LandProtectionType.CONVERTIBLE, midAge, unprotectedArea * prop); - landCoverAreas.get(lcType).setArea(LandProtectionType.PROTECTED, midAge, protectedArea * prop); - /* - for (int a = minAge; a <= maxAge; a++) { - landCoverAreas.get(lcType).setArea(LandProtectionType.CONVERTIBLE, a, unprotectedArea * prop); - landCoverAreas.get(lcType).setArea(LandProtectionType.PROTECTED, a, protectedArea * prop); - } - */ - - } - } - - // Check that areas have been allocated correctly - Map<LandCoverType, Double> initAreas = new HashMap<LandCoverType, Double>(); - for (LandCoverType lc : LandCoverType.values()) { - initAreas.put(lc, landCover.getLandCoverArea(lc)); - } - runAreaCheck(initAreas); - } - - - public void runAreaCheck(Map<LandCoverType, Double> lcAreas) { - final double THRESHOLD = 1e-7; - for (LandCoverType lcType : landCoverAreas.keySet()) { - double initArea = lcAreas.get(lcType); - double processedArea = getLandCoverArea(lcType); - if (Math.abs(initArea - processedArea) > THRESHOLD) { - LogWriter.printlnError("LandUseItem: processed area and initial area different: " + lcType.getName() + " " + processedArea + ", " + initArea); - } - } - } - - public void setUnavailableArea(double unavailF) { - double barrenAndUrban = getLandCoverArea(LandCoverType.BARREN) + getLandCoverArea(LandCoverType.URBAN); - double unavailableArea = unavailF * getTotalLandCoverArea(); - double shortfall = Math.max(0.0, unavailableArea - barrenAndUrban); - if (shortfall == 0.0) - return; - - double convertibleNatural = getConvertibleLandCoverArea(LandCoverType.NATURAL); - double protectedNatural = getProtectedLandCoverArea(LandCoverType.NATURAL); - double convertibleTimberF = getConvertibleLandCoverArea(LandCoverType.TIMBER_FOREST); - double convertiblePasture = getConvertibleLandCoverArea(LandCoverType.PASTURE); - double convertibleCropland = getConvertibleLandCoverArea(LandCoverType.CROPLAND); - - double unavailFromConvertibleNatural = Math.min(shortfall, convertibleNatural); - shortfall -= unavailFromConvertibleNatural; - if (unavailFromConvertibleNatural > 0) { - landCoverAreas.get(LandCoverType.NATURAL).addArea(LandProtectionType.UNAVAILABLE, unavailFromConvertibleNatural); - landCoverAreas.get(LandCoverType.NATURAL).removeArea(LandProtectionType.CONVERTIBLE, unavailFromConvertibleNatural); - } - - double unavailFromProtectedNatural = Math.min(shortfall, protectedNatural); - shortfall -= unavailFromProtectedNatural; - if (unavailFromProtectedNatural > 0) { - landCoverAreas.get(LandCoverType.NATURAL).addArea(LandProtectionType.UNAVAILABLE, unavailFromProtectedNatural); - landCoverAreas.get(LandCoverType.NATURAL).removeArea(LandProtectionType.PROTECTED, unavailFromProtectedNatural); - } - - double unavailFromTimberF = Math.min(shortfall, convertibleTimberF); - shortfall -= unavailFromTimberF; - if (unavailFromTimberF > 0) { - landCoverAreas.get(LandCoverType.TIMBER_FOREST).addArea(LandProtectionType.UNAVAILABLE, unavailFromTimberF); - landCoverAreas.get(LandCoverType.TIMBER_FOREST).removeArea(LandProtectionType.CONVERTIBLE, unavailFromTimberF); - } - - double unavailFromPasture = Math.min(shortfall, convertiblePasture); - shortfall -= unavailFromPasture; - if (unavailFromPasture > 0) { - landCoverAreas.get(LandCoverType.PASTURE).addArea(LandProtectionType.UNAVAILABLE, unavailFromPasture); - landCoverAreas.get(LandCoverType.PASTURE).removeArea(LandProtectionType.CONVERTIBLE, unavailFromPasture); - } - - double unavailFromCropland = Math.min(shortfall, convertibleCropland); - shortfall -= unavailFromCropland; - if (unavailFromCropland > 0) { - landCoverAreas.get(LandCoverType.CROPLAND).addArea(LandProtectionType.UNAVAILABLE, unavailFromCropland); - landCoverAreas.get(LandCoverType.CROPLAND).removeArea(LandProtectionType.CONVERTIBLE, unavailFromCropland); - } - - if (shortfall > 0) { - LogWriter.printlnWarning("LandUseItem.setUnavailableArea insufficient area to make unavailable"); - } - } - - public Map<LandCoverType, LandCoverTile> getLandCoverTiles() { - return landCoverAreas; - } - - public void setProtectedFraction(double protFrac) { - if (!ModelConfig.PROTECTED_AREAS_ENABLED) { - this.protectedFraction = 0; - return; - } - if (!Double.isNaN(ModelConfig.CONSTANT_PROTECTED_AREA_RATE)) { - this.protectedFraction = ModelConfig.CONSTANT_PROTECTED_AREA_RATE; // TODO is this a fraction or area? - } else { - this.protectedFraction = Math.max(protFrac, ModelConfig.MIN_NATURAL_RATE); - } - } - - public void setProtectedLandCoverArea(LandCoverType lcType, double area) { - landCoverAreas.get(lcType).setArea(LandProtectionType.PROTECTED, 0, area); - } - - public double getProtectedFraction() { - return protectedFraction; - } - - public double getTotalProtectedArea() { - if(ModelConfig.PROTECTED_AREAS_ENABLED) - return landCoverAreas.values().stream().mapToDouble(o -> o.getTotalArea(LandProtectionType.PROTECTED)).sum(); - else - return 0.0; - } - - public double getProtectedLandCoverArea(LandCoverType lcType) { - return landCoverAreas.get(lcType).getTotalArea(LandProtectionType.PROTECTED); - } - - public double getSuitableArea() { - double suitableArea = 0; - for (LandCoverType lcType : LandCoverType.getConvertibleTypes()) { - suitableArea += getConvertibleLandCoverArea(lcType); - } - return suitableArea; - } - - /** move areas from one land cover to another, return any residual not possible */ - public double moveAreas(LandCoverType toType, LandCoverType fromType, double area) { - if (area == 0) - return 0; - - double prevFrom = getConvertibleLandCoverArea(fromType); - double areaMoved; - - areaMoved = Math.min(area, prevFrom); - - if (Math.abs(area - areaMoved) / area > 1e-6) { - throw new RuntimeException("Move unprotected area shortfall. Should not happen due to GAMS constraint."); - } - - // Note if toType == fromType, area will be removed from fromType and added back in with age 0 - LandCoverTile toTiles = landCoverAreas.get(toType); - LandCoverTile fromTiles = landCoverAreas.get(fromType); - - fromTiles.removeArea(LandProtectionType.CONVERTIBLE, area); - toTiles.addArea(LandProtectionType.CONVERTIBLE, area); - - return area - areaMoved; - } - - public double getLandCoverArea(LandCoverType c) { - Double d = landCoverAreas.get(c).getTotalArea(); - return d == null ? 0.0 : d; - } - - public double getLandCoverFract(LandCoverType c) { - double totalArea = getTotalLandCoverArea(); - return totalArea==0 ? 0.0 : getLandCoverArea(c) / totalArea; - } - - public void setConvertibleLandCoverArea(LandCoverType c, double d) { - if (Double.isNaN(d) || Double.isInfinite(d)) - throw new RuntimeException("AreasItem for " + c + " is " + d); - - double landCover = (d < 0.0) ? 0.0 : d; - - landCoverAreas.get(c).removeAllArea(); - landCoverAreas.get(c).addArea(LandProtectionType.CONVERTIBLE, landCover);; - } - - public double getTotalLandCoverArea() { - double d = 0; - for (LandCoverType l : LandCoverType.values()) - d += getLandCoverArea(l); - - return d; - } - - public Map<LandCoverType, Double> getLandCoverAreaMap() { - Map<LandCoverType, Double> areaMap = new HashMap<LandCoverType, Double>(); - for (LandCoverType lcType : LandCoverType.values()) { - areaMap.put(lcType, getLandCoverArea(lcType)); - } - return areaMap; - } - - public double getTotalConvertibleNaturalArea() { - double unprotectedNatural = 0; - for (LandCoverType landType : LandCoverType.getNaturalTypes()) { - unprotectedNatural += landCoverAreas.get(landType).getTotalArea(LandProtectionType.CONVERTIBLE); - } - return unprotectedNatural; - } - - public double getConvertibleLandCoverArea(LandCoverType c) { - return landCoverAreas.get(c).getTotalArea(LandProtectionType.CONVERTIBLE); - } - - - public void updateProtectedFraction(int year) { - if (!ModelConfig.FORCE_PROTECTED_AREAS) - return; - - if (year >= ModelConfig.FORCE_PROTECTED_AREAS_START_YEAR && year < ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR) - protectedFraction = 1.0 / (ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR - year); - - if (year >= ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR) - protectedFraction = 1.0; - } - - public Map<CropType, Intensity> getIntensityMap() { - return intensityMap; - } - - public Intensity getIntensity(CropType crop) { - return intensityMap.get(crop); - } - - public void setIntensity(CropType crop, Intensity intensityData) { - intensityMap.put(crop, intensityData); - } - - /** Fertiliser rate in kg/ha */ - public double getFertiliserRate(CropType crop) { - Intensity i = getIntensity(crop); - return (i == null) ? 0 : i.getFertiliserAmount(); - } - - /** Fertiliser amount in kt (1000 tonnes), for this location */ - public double getFertiliserAmount(CropType c) { - double rate = getFertiliserRate(c); - double area = getCropArea(c); - return rate * area; - } - - public double getFertiliserAverageRate(CropType... crops) { - double fertTotal = 0; - double areaTotal = 0; - - for (CropType c : crops) { - fertTotal += getFertiliserAmount(c); - areaTotal += getCropArea(c); - } - - return areaTotal > 0 ? fertTotal / areaTotal : 0; - } - - public static double getFertiliserTotal(Collection<? extends LandUseItem> items, CropType... crops) { - double total = 0; - for (LandUseItem a : items) { - if (a == null) - continue; - - for (CropType c : crops) - total += a.getFertiliserAmount(c); - } - - return total; - } - - public static double getFertiliserTotal(Collection<? extends LandUseItem> items, Collection<CropType> crops) { - return getFertiliserTotal(items, crops.toArray(new CropType[crops.size()])); - } - - /** Irrigation Intensity (unit less) */ - public double getIrrigationIntensity(CropType crop) { - Intensity i = getIntensity(crop); - return (i == null) ? 0 : i.getIrrigationIntensity(); - } - - /** Irrigation rate in litre/m2 */ - public double getIrrigationRate(CropType crop) { - Intensity i = getIntensity(crop); - - return (i == null) ? 0 : i.getIrrigationRate(); - } - - /** Irrigation amount in km3 or 10^9 m3, for this location */ - public double getIrrigationAmount(CropType c) { - double rate = getIrrigationRate(c); - double area = getCropArea(c); - return rate * area * 0.01; // rate(10^-3m or mm or l/m2) * area(10^6ha) = 10^3 m.ha = 10^7 m3 = 1/100 km3 - } - - /** Irrigation Intensity (unit less) */ - public double getIrrigationAverageIntensity(CropType... crops) { - double irrigTotal = 0; - double areaTotal = 0; - - for (CropType c : crops) { - double area = getCropArea(c); - irrigTotal += getIrrigationIntensity(c) * area; - areaTotal += area; - } - - return areaTotal > 0 ? irrigTotal / areaTotal : 0; - } - - /** Irrigation amount in km3, for this location */ - public static double getIrrigationTotal(Collection<? extends LandUseItem> items, CropType... crops) { - double total = 0; - for (LandUseItem a : items) { - if (a == null) - continue; - - for (CropType c : crops) - total += a.getIrrigationAmount(c); - } - - return total; - } - - public static double getIrrigationTotal(Collection<? extends LandUseItem> items, Collection<CropType> crops) { - return getIrrigationTotal(items, crops.toArray(new CropType[crops.size()])); - } - - public double getCropArea(CropType c) { - Double d = getLandCoverArea(LandCoverType.CROPLAND) * getCropFraction(c); - return d; - } - - public double getCropFraction(CropType c) { - Double d = cropFractions.get(c); - return d == null ? 0.0 : d; - } - - public double getCropFraction(CropType... cropsToFind) { - double totalFract = 0; - for (CropType crop : cropsToFind) { - totalFract += getCropFraction(crop); - } - return totalFract; - } - - public Map<CropType, Double> getCropFractionMap() { - return cropFractions; - } - - public void setCropFraction(CropType c, double areaFract) { - cropFractions.put(c, areaFract); - } - - public static double getAbandonedPasture(Collection<? extends LandUseItem> landUses) { - double total = 0; - for (LandUseItem a : landUses) { - if (a!=null) { - Double d = a.getLandCoverArea(LandCoverType.PASTURE); - Intensity intensity = a.getIntensity(CropType.PASTURE); - if (intensity!=null && d!=null && intensity.getFertiliserAmount()==0 && intensity.getIrrigationRate()==0 && intensity.getOtherIntensity()==0) - total += d; - } - } - return total; - } - - public static double getSuitableTotal(Collection<? extends LandUseItem> landUses, int year) { - double total = 0; - for (LandUseItem a : landUses) { - if (a!=null) { - Double suitable = a.getSuitableArea(); - total += suitable; - } - } - return total; - } - - public CropToDouble getCropChanges(LandUseItem prevAreaAggItem) { - CropToDouble changes = new CropToDouble(); - - for (CropType c : CropType.getCropsLessPasture()) { - double change = getCropArea(c) - prevAreaAggItem.getCropArea(c); - changes.put(c, change); - } - - return changes; - } - - public void doForestRotation(int rotationAge) { - LandCoverTile tiles = landCoverAreas.get(LandCoverType.TIMBER_FOREST); - for (int a=rotationAge; a<=LandCoverTile.getMaxAgeBin(); a++) { - tiles.resetAreaForAge(LandProtectionType.CONVERTIBLE, a); - } - } - - public void incrementTilesAge() { - for (LandCoverTile tile : landCoverAreas.values()) { - tile.incrementAge(); - } - } - - private boolean isZeroOrNull(Double d) { - return d == null || d == 0; - } - - @Override - public void interpolateAll(LandUseItem fromItem, LandUseItem toItem, double factor) { - cropFractions = new HashMap<CropType, Double>(); - landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); - - Double fromCropCover = fromItem.landCoverAreas.get(LandCoverType.CROPLAND).getTotalArea(); - Double toCropCover = toItem.landCoverAreas.get(LandCoverType.CROPLAND).getTotalArea(); - - if (!isZeroOrNull(fromCropCover) && isZeroOrNull(toCropCover)) { // if start with crop but end with none, take starting crop fractions - cropFractions.putAll(fromItem.cropFractions); - intensityMap.putAll(fromItem.intensityMap); - } - else if (isZeroOrNull(fromCropCover) && !isZeroOrNull(toCropCover)) { // if start with no crop but end with some, take end crop fractions - cropFractions.putAll(toItem.cropFractions); - intensityMap.putAll(toItem.intensityMap); - } - else { // otherwise we need to interpolate crop fractions - for (CropType crop : CropType.values()) { - Double from = fromItem.cropFractions.get(crop); - Double to = toItem.cropFractions.get(crop); - Double d = Interpolator.interpolate(from, to, factor); - cropFractions.put(crop, d); - - Intensity fromIntensity = fromItem.intensityMap.get(crop); - Intensity toIntensity = toItem.intensityMap.get(crop); - Intensity interpolateIntensity = toIntensity; // might still be null - - if (fromIntensity != null && toIntensity != null) - interpolateIntensity = new Intensity(fromIntensity, toIntensity, factor); // both non-null really interpolate - else if (fromIntensity != null) - interpolateIntensity = fromIntensity; // just fromIntensity non-null - - intensityMap.put(crop, interpolateIntensity); - } - } - - for (LandCoverType landCover : LandCoverType.values()) { - for (int age = 0; age <= LandCoverTile.getMaxAgeBin(); age++) { - Double from = fromItem.landCoverAreas.get(landCover).getArea(LandProtectionType.CONVERTIBLE, age); - Double to = toItem.landCoverAreas.get(landCover).getArea(LandProtectionType.CONVERTIBLE, age); - Double d = Interpolator.interpolate(from, to, factor); - LandCoverTile tile = landCoverAreas.computeIfAbsent(landCover, k -> new LandCoverTile()); - tile.setArea(LandProtectionType.CONVERTIBLE, age, d); - } - } - - for (LandCoverType landCover : LandCoverType.values()) { - Double from = fromItem.landCoverAreas.get(landCover).getTotalArea(LandProtectionType.PROTECTED); - Double to = toItem.landCoverAreas.get(landCover).getTotalArea(LandProtectionType.PROTECTED); - Double d = Interpolator.interpolate(from, to, factor); - LandCoverTile tile = landCoverAreas.computeIfAbsent(landCover, k -> new LandCoverTile()); - tile.addArea(LandProtectionType.PROTECTED, d); - } - } - - public static double getTotalLandCover(Collection<? extends LandUseItem> landUses, LandCoverType landCover) { - double total = 0; - for (LandUseItem a : landUses) { - if (a!=null) { - Double d = a.getLandCoverArea(landCover); - if (d!=null) - total += d; - } - } - return total; - } - - public static double getTotalLandArea(Collection<? extends LandUseItem> landUses) { - double total = 0; - for (LandUseItem a : landUses) { - if (a!=null) { - Double d = a.getTotalLandCoverArea(); - if (d!=null) - total += d; - } - } - return total; - } - - public static double getTotalCropArea(Collection<LandUseItem> landUses, CropType crop) { - double total = 0; - for (LandUseItem a : landUses) - total += a.getCropArea(crop); - - return total; - } - - @Override - public String toString() { - Map<LandCoverType, Double> convertibleAreas = new HashMap<LandCoverType, Double>(); - Map<LandCoverType, Double> protectedAreas = new HashMap<LandCoverType, Double>(); - for (LandCoverType lcType : LandCoverType.values()) { - convertibleAreas.put(lcType, landCoverAreas.get(lcType).getTotalArea(LandProtectionType.CONVERTIBLE)); - protectedAreas.put(lcType, landCoverAreas.get(lcType).getTotalArea(LandProtectionType.PROTECTED)); - } - return "LandUseItem: [landCoverAreas=" + convertibleAreas + ", protectedArea=" + protectedAreas + "]"; - } +package ac.ed.lurg.landuse; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.types.CropToDouble; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.ed.lurg.utils.Interpolator; +import ac.sac.raster.InterpolatingRasterItem; + +public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serializable { + private static final long serialVersionUID = 763526147224355158L; + + private Map<CropType, Intensity> intensityMap = new HashMap<CropType, Intensity>(); + private Map<CropType, Double> cropFractions = new HashMap<CropType, Double>(); + private Map<LandCoverType, LandCoverTile> landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); + private double protectedFraction; // protected fraction of total area + + public LandUseItem() { + for (LandCoverType lcType : LandCoverType.values()) { + landCoverAreas.put(lcType, new LandCoverTile()); + } + } + + public LandUseItem(LandCoverItem landCover) { + this(); + if (landCover != null) { + setCropFraction(CropType.WHEAT, 0.5); // random start as don't have better data + setCropFraction(CropType.MAIZE, 0.5); + setProtectedFraction(landCover.getProtectedFraction()); + this.landCoverAreas.putAll(landCover.getLandCoverTiles()); + } + } + + public LandUseItem(LandUseItem luItemToCopy) { + this(); + intensityMap.putAll(luItemToCopy.intensityMap); + cropFractions.putAll(luItemToCopy.cropFractions); + landCoverAreas.putAll(luItemToCopy.landCoverAreas); + protectedFraction = (luItemToCopy.protectedFraction); + } + + public void overwriteLandCoverAreas(Map<LandCoverType, LandCoverTile> landCoverAreas) { + this.landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); + this.landCoverAreas.putAll(landCoverAreas); + } + + public Map<LandCoverType, LandCoverTile> getLandCoverTiles() { + return landCoverAreas; + } + + public void setProtectedFraction(double protFrac) { + if (!ModelConfig.PROTECTED_AREAS_ENABLED) { + this.protectedFraction = 0; + return; + } + if (!Double.isNaN(ModelConfig.CONSTANT_PROTECTED_AREA_RATE)) { + this.protectedFraction = ModelConfig.CONSTANT_PROTECTED_AREA_RATE; + } else { + this.protectedFraction = Math.max(protFrac, ModelConfig.MIN_NATURAL_RATE); + } + } + + public double getProtectedFraction() { + return protectedFraction; + } + + public double getLandCoverArea(LandCoverType lcType, LandProtectionType lpType) { + return landCoverAreas.get(lcType).getTotalArea(lpType); + } + + public double getLandCoverArea(LandCoverType lcType) { + Double d = landCoverAreas.get(lcType).getTotalArea(); + return d == null ? 0.0 : d; + } + + public double getTotalLandCoverArea() { + double d = 0; + for (LandCoverType l : LandCoverType.values()) + d += getLandCoverArea(l); + + return d; + } + + public double getTotalLandCoverArea(LandProtectionType lpType) { + double d = 0; + for (LandCoverType l : LandCoverType.values()) + d += getLandCoverArea(l, lpType); + + return d; + } + + public double getLandCoverFract(LandCoverType c) { + double totalArea = getTotalLandCoverArea(); + return totalArea==0 ? 0.0 : getLandCoverArea(c) / totalArea; + } + + public double getSuitableArea() { + double suitableArea = 0; + for (LandCoverType lcType : LandCoverType.getConvertibleTypes()) { + suitableArea += getLandCoverArea(lcType, LandProtectionType.CONVERTIBLE); + } + return suitableArea; + } + + /** move areas from one land cover to another, return any residual not possible */ + public double moveAreas(LandCoverType toType, LandCoverType fromType, double area) { + if (area == 0) + return 0; + + double prevFrom = getLandCoverArea(fromType, LandProtectionType.CONVERTIBLE); + double areaMoved; + + areaMoved = Math.min(area, prevFrom); + + if (Math.abs(area - areaMoved) / area > 1e-6) { + throw new RuntimeException("Move unprotected area shortfall. Should not happen due to GAMS constraint."); + } + + // Note if toType == fromType, area will be removed from fromType and added back in with age 0 + LandCoverTile toTiles = landCoverAreas.get(toType); + LandCoverTile fromTiles = landCoverAreas.get(fromType); + + fromTiles.removeArea(LandProtectionType.CONVERTIBLE, areaMoved); + if (ModelConfig.IS_CALIBRATION_RUN) { + toTiles.addArea(LandProtectionType.CONVERTIBLE, areaMoved); // preserve age distribution + } else { + toTiles.addArea(LandProtectionType.CONVERTIBLE, 0, areaMoved); // new area starts at age=0 + } + + return area - areaMoved; + } + + public void setLandCoverArea(LandCoverType c, LandProtectionType p, double d) { + if (Double.isNaN(d) || Double.isInfinite(d)) + throw new RuntimeException("AreasItem for " + c + " is " + d); + + double landCover = (d < 0.0) ? 0.0 : d; + + landCoverAreas.get(c).removeAllArea(p); + landCoverAreas.get(c).addArea(p, 0, landCover); + } + + public Map<LandCoverType, Double> getLandCoverAreaMap() { + Map<LandCoverType, Double> areaMap = new HashMap<LandCoverType, Double>(); + for (LandCoverType lcType : LandCoverType.values()) { + areaMap.put(lcType, getLandCoverArea(lcType)); + } + return areaMap; + } + + public void updateProtectedFraction(int year) { + if (!ModelConfig.FORCE_PROTECTED_AREAS) + return; + + if (year >= ModelConfig.FORCE_PROTECTED_AREAS_START_YEAR && year < ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR) + protectedFraction = 1.0 / (ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR - year); + + if (year >= ModelConfig.FORCE_PROTECTED_AREAS_END_YEAR) + protectedFraction = 1.0; + + updateProtectedArea(); + } + + private void updateProtectedArea() { + + double previousProtectedTotal = landCoverAreas.values().stream().mapToDouble(o -> o.getTotalArea(LandProtectionType.PROTECTED)).sum(); + double newProtectedTotal = protectedFraction * getTotalLandCoverArea(); + double factor = newProtectedTotal / previousProtectedTotal; + + for (LandCoverType lcType : LandCoverType.values()) { + double prevArea = getLandCoverArea(lcType, LandProtectionType.PROTECTED); + double newArea = prevArea * factor; + if (newArea > prevArea) { + landCoverAreas.get(lcType).moveArea(LandProtectionType.CONVERTIBLE, LandProtectionType.PROTECTED, newArea); + } else { + landCoverAreas.get(lcType).moveArea(LandProtectionType.PROTECTED, LandProtectionType.CONVERTIBLE, newArea); + } + } + } + + public Map<CropType, Intensity> getIntensityMap() { + return intensityMap; + } + + public Intensity getIntensity(CropType crop) { + return intensityMap.get(crop); + } + + public void setIntensity(CropType crop, Intensity intensityData) { + intensityMap.put(crop, intensityData); + } + + /** Fertiliser rate in kg/ha */ + public double getFertiliserRate(CropType crop) { + Intensity i = getIntensity(crop); + return (i == null) ? 0 : i.getFertiliserAmount(); + } + + /** Fertiliser amount in kt (1000 tonnes), for this location */ + public double getFertiliserAmount(CropType c) { + double rate = getFertiliserRate(c); + double area = getCropArea(c); + return rate * area; + } + + public double getFertiliserAverageRate(CropType... crops) { + double fertTotal = 0; + double areaTotal = 0; + + for (CropType c : crops) { + fertTotal += getFertiliserAmount(c); + areaTotal += getCropArea(c); + } + + return areaTotal > 0 ? fertTotal / areaTotal : 0; + } + + public static double getFertiliserTotal(Collection<? extends LandUseItem> items, CropType... crops) { + double total = 0; + for (LandUseItem a : items) { + if (a == null) + continue; + + for (CropType c : crops) + total += a.getFertiliserAmount(c); + } + + return total; + } + + public static double getFertiliserTotal(Collection<? extends LandUseItem> items, Collection<CropType> crops) { + return getFertiliserTotal(items, crops.toArray(new CropType[crops.size()])); + } + + /** Irrigation Intensity (unit less) */ + public double getIrrigationIntensity(CropType crop) { + Intensity i = getIntensity(crop); + return (i == null) ? 0 : i.getIrrigationIntensity(); + } + + /** Irrigation rate in litre/m2 */ + public double getIrrigationRate(CropType crop) { + Intensity i = getIntensity(crop); + + return (i == null) ? 0 : i.getIrrigationRate(); + } + + /** Irrigation amount in km3 or 10^9 m3, for this location */ + public double getIrrigationAmount(CropType c) { + double rate = getIrrigationRate(c); + double area = getCropArea(c); + return rate * area * 0.01; // rate(10^-3m or mm or l/m2) * area(10^6ha) = 10^3 m.ha = 10^7 m3 = 1/100 km3 + } + + /** Irrigation Intensity (unit less) */ + public double getIrrigationAverageIntensity(CropType... crops) { + double irrigTotal = 0; + double areaTotal = 0; + + for (CropType c : crops) { + double area = getCropArea(c); + irrigTotal += getIrrigationIntensity(c) * area; + areaTotal += area; + } + + return areaTotal > 0 ? irrigTotal / areaTotal : 0; + } + + /** Irrigation amount in km3, for this location */ + public static double getIrrigationTotal(Collection<? extends LandUseItem> items, CropType... crops) { + double total = 0; + for (LandUseItem a : items) { + if (a == null) + continue; + + for (CropType c : crops) + total += a.getIrrigationAmount(c); + } + + return total; + } + + public static double getIrrigationTotal(Collection<? extends LandUseItem> items, Collection<CropType> crops) { + return getIrrigationTotal(items, crops.toArray(new CropType[crops.size()])); + } + + public double getCropArea(CropType c) { + Double d = getLandCoverArea(LandCoverType.CROPLAND) * getCropFraction(c); + return d; + } + + public double getCropFraction(CropType c) { + Double d = cropFractions.get(c); + return d == null ? 0.0 : d; + } + + public double getCropFraction(CropType... cropsToFind) { + double totalFract = 0; + for (CropType crop : cropsToFind) { + totalFract += getCropFraction(crop); + } + return totalFract; + } + + public Map<CropType, Double> getCropFractionMap() { + return cropFractions; + } + + public void setCropFraction(CropType c, double areaFract) { + cropFractions.put(c, areaFract); + } + + public static double getAbandonedPasture(Collection<? extends LandUseItem> landUses) { + double total = 0; + for (LandUseItem a : landUses) { + if (a!=null) { + Double d = a.getLandCoverArea(LandCoverType.PASTURE); + Intensity intensity = a.getIntensity(CropType.PASTURE); + if (intensity!=null && d!=null && intensity.getFertiliserAmount()==0 && intensity.getIrrigationRate()==0 && intensity.getOtherIntensity()==0) + total += d; + } + } + return total; + } + + public static double getSuitableTotal(Collection<? extends LandUseItem> landUses, int year) { + double total = 0; + for (LandUseItem a : landUses) { + if (a!=null) { + Double suitable = a.getSuitableArea(); + total += suitable; + } + } + return total; + } + + public CropToDouble getCropChanges(LandUseItem prevAreaAggItem) { + CropToDouble changes = new CropToDouble(); + + for (CropType c : CropType.getCropsLessPasture()) { + double change = getCropArea(c) - prevAreaAggItem.getCropArea(c); + changes.put(c, change); + } + + return changes; + } + + public void incrementTilesAge() { + for (LandCoverTile tile : landCoverAreas.values()) { + tile.cleanUp(); + tile.incrementAge(); + } + } + + private boolean isZeroOrNull(Double d) { + return d == null || d == 0; + } + + @Override + public void interpolateAll(LandUseItem fromItem, LandUseItem toItem, double factor) { + cropFractions = new HashMap<CropType, Double>(); + landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); + + Double fromCropCover = fromItem.landCoverAreas.get(LandCoverType.CROPLAND).getTotalArea(); + Double toCropCover = toItem.landCoverAreas.get(LandCoverType.CROPLAND).getTotalArea(); + + if (!isZeroOrNull(fromCropCover) && isZeroOrNull(toCropCover)) { // if start with crop but end with none, take starting crop fractions + cropFractions.putAll(fromItem.cropFractions); + intensityMap.putAll(fromItem.intensityMap); + } + else if (isZeroOrNull(fromCropCover) && !isZeroOrNull(toCropCover)) { // if start with no crop but end with some, take end crop fractions + cropFractions.putAll(toItem.cropFractions); + intensityMap.putAll(toItem.intensityMap); + } + else { // otherwise we need to interpolate crop fractions + for (CropType crop : CropType.values()) { + Double from = fromItem.cropFractions.get(crop); + Double to = toItem.cropFractions.get(crop); + Double d = Interpolator.interpolate(from, to, factor); + cropFractions.put(crop, d); + + Intensity fromIntensity = fromItem.intensityMap.get(crop); + Intensity toIntensity = toItem.intensityMap.get(crop); + Intensity interpolateIntensity = toIntensity; // might still be null + + if (fromIntensity != null && toIntensity != null) + interpolateIntensity = new Intensity(fromIntensity, toIntensity, factor); // both non-null really interpolate + else if (fromIntensity != null) + interpolateIntensity = fromIntensity; // just fromIntensity non-null + + intensityMap.put(crop, interpolateIntensity); + } + } + + for (LandCoverType landCover : LandCoverType.values()) { + for (int age : landCoverAreas.get(landCover).getAgeKeys()) { + Double from = fromItem.landCoverAreas.get(landCover).getArea(LandProtectionType.CONVERTIBLE, age); + Double to = toItem.landCoverAreas.get(landCover).getArea(LandProtectionType.CONVERTIBLE, age); + Double d = Interpolator.interpolate(from, to, factor); + LandCoverTile tile = landCoverAreas.computeIfAbsent(landCover, k -> new LandCoverTile()); + tile.setArea(LandProtectionType.CONVERTIBLE, age, d); + } + } + + for (LandCoverType landCover : LandCoverType.values()) { + Double from = fromItem.landCoverAreas.get(landCover).getTotalArea(LandProtectionType.PROTECTED); + Double to = toItem.landCoverAreas.get(landCover).getTotalArea(LandProtectionType.PROTECTED); + Double d = Interpolator.interpolate(from, to, factor); + LandCoverTile tile = landCoverAreas.computeIfAbsent(landCover, k -> new LandCoverTile()); + tile.addArea(LandProtectionType.PROTECTED, 0, d); + } + } + + public static double getTotalLandCover(Collection<? extends LandUseItem> landUses, LandCoverType landCover) { + double total = 0; + for (LandUseItem a : landUses) { + if (a!=null) { + Double d = a.getLandCoverArea(landCover); + if (d!=null) + total += d; + } + } + return total; + } + + public static double getTotalLandArea(Collection<? extends LandUseItem> landUses) { + double total = 0; + for (LandUseItem a : landUses) { + if (a!=null) { + Double d = a.getTotalLandCoverArea(); + if (d!=null) + total += d; + } + } + return total; + } + + public static double getTotalCropArea(Collection<LandUseItem> landUses, CropType crop) { + double total = 0; + for (LandUseItem a : landUses) + total += a.getCropArea(crop); + + return total; + } + + @Override + public String toString() { + Map<LandCoverType, Double> convertibleAreas = new HashMap<LandCoverType, Double>(); + Map<LandCoverType, Double> protectedAreas = new HashMap<LandCoverType, Double>(); + for (LandCoverType lcType : LandCoverType.values()) { + convertibleAreas.put(lcType, landCoverAreas.get(lcType).getTotalArea(LandProtectionType.CONVERTIBLE)); + protectedAreas.put(lcType, landCoverAreas.get(lcType).getTotalArea(LandProtectionType.PROTECTED)); + } + return "LandUseItem: [landCoverAreas=" + convertibleAreas + ", protectedArea=" + protectedAreas + "]"; + } } \ No newline at end of file diff --git a/src/ac/ed/lurg/landuse/LandUseReader.java b/src/ac/ed/lurg/landuse/LandUseReader.java index bc8963300ff9d93b8d42c0c40ffd99df0566d548..48f278d16a4cdf7a994e59ca22f3c7492c587224 100644 --- a/src/ac/ed/lurg/landuse/LandUseReader.java +++ b/src/ac/ed/lurg/landuse/LandUseReader.java @@ -1,41 +1,42 @@ -package ac.ed.lurg.landuse; - -import java.util.Map; - -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.sac.raster.AbstractTabularRasterReader; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -// This class is currently not use as using serialization instead, but could be handy at some stage -public class LandUseReader extends AbstractTabularRasterReader<LandUseItem> { - - private static final int MIN_COLS = 20; - - public LandUseReader(RasterSet<LandUseItem> landCover) { - super(",", MIN_COLS, landCover); - } - - @Override - protected void setData(RasterKey key, LandUseItem luData, Map<String, Double> rowValues) { - - for (LandCoverType cover : LandCoverType.values()) { - luData.setConvertibleLandCoverArea(cover, getValueForCol(rowValues, cover.getName())); - } - - luData.setProtectedFraction(getValueForCol(rowValues, "protected") / luData.getTotalLandCoverArea()); - - for (CropType crop : CropType.getAllItems()) { - double cropFract = getValueForCol(rowValues, crop.getGamsName() + "_A"); - if (cropFract > 0.0 | CropType.PASTURE.equals(crop)) { // pasture isn't a crop so cropFract will always be zero for it, but still want to set intensities - luData.setCropFraction(crop, cropFract); - double fertI = getValueForCol(rowValues, crop.getGamsName() + "_FI"); - double irrigI = getValueForCol(rowValues, crop.getGamsName() + "_II"); - double otherI = getValueForCol(rowValues, crop.getGamsName() + "_OI"); - Intensity intensity = new Intensity(fertI, irrigI, otherI); - luData.setIntensity(crop, intensity); - } - } - } -} +package ac.ed.lurg.landuse; + +import java.util.Map; + +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.sac.raster.AbstractTabularRasterReader; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterSet; + +// This class is currently not use as using serialization instead, but could be handy at some stage +public class LandUseReader extends AbstractTabularRasterReader<LandUseItem> { + + private static final int MIN_COLS = 20; + + public LandUseReader(RasterSet<LandUseItem> landCover) { + super(",", MIN_COLS, landCover); + } + + @Override + protected void setData(RasterKey key, LandUseItem luData, Map<String, Double> rowValues) { + + for (LandCoverType cover : LandCoverType.values()) { + luData.setLandCoverArea(cover, LandProtectionType.CONVERTIBLE, getValueForCol(rowValues, cover.getName())); + } + + luData.setProtectedFraction(getValueForCol(rowValues, "protected") / luData.getTotalLandCoverArea()); + + for (CropType crop : CropType.getAllItems()) { + double cropFract = getValueForCol(rowValues, crop.getGamsName() + "_A"); + if (cropFract > 0.0 | CropType.PASTURE.equals(crop)) { // pasture isn't a crop so cropFract will always be zero for it, but still want to set intensities + luData.setCropFraction(crop, cropFract); + double fertI = getValueForCol(rowValues, crop.getGamsName() + "_FI"); + double irrigI = getValueForCol(rowValues, crop.getGamsName() + "_II"); + double otherI = getValueForCol(rowValues, crop.getGamsName() + "_OI"); + Intensity intensity = new Intensity(fertI, irrigI, otherI); + luData.setIntensity(crop, intensity); + } + } + } +} diff --git a/src/ac/ed/lurg/landuse/LandUseSerializer.java b/src/ac/ed/lurg/landuse/LandUseSerializer.java deleted file mode 100644 index 7f2d3091ba96d27954e67cc9994a6108a01c4b53..0000000000000000000000000000000000000000 --- a/src/ac/ed/lurg/landuse/LandUseSerializer.java +++ /dev/null @@ -1,270 +0,0 @@ -package ac.ed.lurg.landuse; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.stream.FactoryConfigurationError; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.types.LandProtectionType; -import ac.ed.lurg.utils.LogWriter; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -public class LandUseSerializer { - - XMLStreamWriter xmlWriter; - - public void LandUseSerialiser() {} - - public void serialiseLandUse(RasterSet<LandUseItem> luRaster) { - - try { - - OutputStream outStream = new FileOutputStream(new File(ModelConfig.SERIALIZED_LAND_COVER_FILE)); - - xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter( - new OutputStreamWriter(outStream, "utf-8")); - - xmlWriter.writeStartDocument("UTF-8", "1.0"); - xmlWriter.writeStartElement("LandUseItems"); - - int counter = 1; - for (Map.Entry<RasterKey, LandUseItem> entry : luRaster.entrySet()) { - RasterKey key = entry.getKey(); - LandUseItem luItem = entry.getValue(); - - xmlWriter.writeStartElement("LandUseItem"); - xmlWriter.writeAttribute("id", Integer.toString(counter)); - writeElement("col", key.getCol()); - writeElement("row", key.getRow()); - - //writeIntensityData(luItem.getIntensityMap()); - //writeCropData(luItem.getCropFractionMap()); - writeLandCoverData(luItem.getLandCoverTiles()); - //writeElement("protectedFraction", luItem.getProtectedFraction()); - - xmlWriter.writeEndElement(); // LandUseItem - xmlWriter.flush(); - counter++; - //if (counter > 1) break; - } - - xmlWriter.writeEndElement(); // LandUseItems - xmlWriter.writeEndDocument(); - - xmlWriter.flush(); - xmlWriter.close(); - - } catch (FileNotFoundException e) { - LogWriter.print(e); - } catch (XMLStreamException e) { - LogWriter.print(e); - } catch (UnsupportedEncodingException e) { - LogWriter.print(e); - } catch (FactoryConfigurationError e) { - LogWriter.print(e.getException()); - } - } - - private void writeIntensityData(Map<CropType, Intensity> intensityMap) { - try { - xmlWriter.writeStartElement("intensity"); - for (Map.Entry<CropType, Intensity> entry : intensityMap.entrySet()) { - CropType crop = entry.getKey(); - Intensity inten = entry.getValue(); - if (inten == null) { - continue; - } - xmlWriter.writeStartElement(crop.getGamsName()); - writeElement("fertiliserIntensity", inten.getFertiliserIntensity()); - writeElement("irrigationIntensity", inten.getIrrigationIntensity()); - writeElement("otherIntensity", inten.getOtherIntensity()); - writeElement("yield", inten.getYield()); - writeElement("unitEnergy", inten.getUnitEnergy()); - writeElement("maxIrrigRate", inten.getMaxIrrigRate()); - xmlWriter.writeEndElement(); - } - xmlWriter.writeEndElement(); - - - } catch (XMLStreamException e) { - LogWriter.print(e); - } - - } - - private void writeCropData(Map<CropType, Double> cropFractMap) { - try { - xmlWriter.writeStartElement("cropFractions"); - for (Map.Entry<CropType, Double> entry : cropFractMap.entrySet()) { - CropType crop = entry.getKey(); - Double fract = entry.getValue(); - if (fract == null) { - continue; - } - writeElement(crop.getGamsName(), fract.doubleValue()); - - } - xmlWriter.writeEndElement(); - - } catch (XMLStreamException e) { - LogWriter.print(e); - } - - } - - private void writeLandCoverData(Map<LandCoverType, LandCoverTile> lcData) { - try { - xmlWriter.writeStartElement("landCover"); - for (Map.Entry<LandCoverType, LandCoverTile> entry : lcData.entrySet()) { - LandCoverType lcType = entry.getKey(); - LandCoverTile tiles = entry.getValue(); - if (tiles == null) { - continue; - } - xmlWriter.writeStartElement(lcType.getName()); - - xmlWriter.writeStartElement("convertible"); - for (int a = 0; a <= LandCoverTile.getMaxAgeBin(); a++) { - double area = tiles.getArea(LandProtectionType.CONVERTIBLE, a); - if (area == 0) - continue; - writeElement("X"+Integer.toString(a), area); - } - xmlWriter.writeEndElement(); - - xmlWriter.writeStartElement("protected"); - for (int a = 0; a <= LandCoverTile.getMaxAgeBin(); a++) { - double area = tiles.getArea(LandProtectionType.PROTECTED, a); - if (area == 0) - continue; - writeElement("X"+Integer.toString(a), area); - } - xmlWriter.writeEndElement(); - - writeElement("unavailable", tiles.getTotalArea(LandProtectionType.UNAVAILABLE)); - - xmlWriter.writeEndElement(); - } - xmlWriter.writeEndElement(); - - - } catch (XMLStreamException e) { - LogWriter.print(e); - } - - } - - private void writeElement(String element, double value) { - try { - xmlWriter.writeStartElement(element); - xmlWriter.writeCharacters(Double.toString(value)); - xmlWriter.writeEndElement(); - } catch (XMLStreamException e) { - LogWriter.print(e); - } - } - - private void writeElement(String element, int value) { - try { - xmlWriter.writeStartElement(element); - xmlWriter.writeCharacters(Integer.toString(value)); - xmlWriter.writeEndElement(); - } catch (XMLStreamException e) { - LogWriter.print(e); - } - } - - public void deserializeLandUse(RasterSet<LandUseItem> luRaster) { - DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder; - - Document doc; - try { - builder = fact.newDocumentBuilder(); - doc = builder.parse(new File(ModelConfig.SERIALIZED_LAND_COVER_FILE)); - doc.getDocumentElement().normalize(); - - Element root = doc.getDocumentElement(); - NodeList nodeLParent = doc.getElementsByTagName("LandUseItems"); - NodeList nodeL = nodeLParent.item(0).getChildNodes(); - - for (int i = 0; i < nodeL.getLength(); i++) { - Node node = nodeL.item(i); - Map<LandCoverType, LandCoverTile> landCoverAreas = new HashMap<LandCoverType, LandCoverTile>(); - for (LandCoverType lcType: LandCoverType.values()) { - landCoverAreas.put(lcType, new LandCoverTile()); - } - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element itemElement = (Element) node; - int col = Integer.parseInt(itemElement.getElementsByTagName("col").item(0).getTextContent()); // TODO store as attribute - int row = Integer.parseInt(itemElement.getElementsByTagName("row").item(0).getTextContent()); - - NodeList lcNodeListParent = itemElement.getElementsByTagName("landCover"); - NodeList lcNodeList = lcNodeListParent.item(0).getChildNodes(); - Element lcNodeElement = (Element) lcNodeList; - - - for (int j = 0; j < lcNodeList.getLength(); j++) { - Node lcNode = lcNodeList.item(j); - - if (node.getNodeType() == Node.ELEMENT_NODE) { - LandCoverType lcType = LandCoverType.getForName(lcNode.getNodeName()); - Element lcElement = (Element) lcNode; - NodeList convElements = lcElement.getElementsByTagName("convertible").item(0).getChildNodes(); - NodeList protElements = lcElement.getElementsByTagName("protected").item(0).getChildNodes(); - double unavArea = Double.parseDouble(lcElement.getElementsByTagName("unavailable").item(0).getTextContent()); - landCoverAreas.get(lcType).setArea(LandProtectionType.UNAVAILABLE, 0, unavArea); - - for (int k = 0; k < convElements.getLength(); k++) { - Node areaNode = convElements.item(k); - int age = Integer.parseInt(areaNode.getNodeName().replace("X", "")); - //double area = Double.parseDouble(areaNode.getNodeValue()); - double area = Double.parseDouble(areaNode.getChildNodes().item(0).getNodeValue()); - landCoverAreas.get(lcType).setArea(LandProtectionType.CONVERTIBLE, age, area); - } - - for (int k = 0; k < protElements.getLength(); k++) { - Node areaNode = protElements.item(k); - int age = Integer.parseInt(areaNode.getNodeName().replace("X", "")); - //double area = Double.parseDouble(areaNode.getNodeValue()); - double area = Double.parseDouble(areaNode.getChildNodes().item(0).getNodeValue()); - landCoverAreas.get(lcType).setArea(LandProtectionType.PROTECTED, age, area); - } - } - } - luRaster.get(new RasterKey(col, row)).overwriteLandCoverAreas(landCoverAreas); - } - - } - - } catch (SAXException | IOException | ParserConfigurationException e) { - // TODO Auto-generated catch block - LogWriter.print(e);; - } - - } -} diff --git a/src/ac/ed/lurg/landuse/WoodUsageData.java b/src/ac/ed/lurg/landuse/WoodUsageData.java index 2a2d29f86c48b5f3910998b3efcf7fe2c3a98276..d650ef5d85de31830f3d017d73e287c89a9e4d91 100644 --- a/src/ac/ed/lurg/landuse/WoodUsageData.java +++ b/src/ac/ed/lurg/landuse/WoodUsageData.java @@ -1,24 +1,30 @@ -package ac.ed.lurg.landuse; - -import java.io.Serializable; - -public class WoodUsageData implements Serializable { - - private static final long serialVersionUID = -3329080279189782763L; - - 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; - } -} +package ac.ed.lurg.landuse; + +import java.io.Serializable; + +public class WoodUsageData implements Serializable { + + private static final long serialVersionUID = -3329080279189782763L; + + private double harvest; + private double netImport; + private double lucHarvest; + + public WoodUsageData(double harvest, double netImport, double lucHarvest) { + this.harvest = harvest; + this.netImport = netImport; + this.lucHarvest = lucHarvest; + } + + public double getHarvest() { + return harvest; + } + + public double getNetImport() { + return netImport; + } + + public double getLucHarvest() { + return lucHarvest; + } +} diff --git a/src/ac/ed/lurg/landuse/WoodUsageReader.java b/src/ac/ed/lurg/landuse/WoodUsageReader.java index e161e122a393ee3f8abf46c12899be8140567ffa..0a3e50d751cac8816e87ddfbebb641fcd5a211d5 100644 --- a/src/ac/ed/lurg/landuse/WoodUsageReader.java +++ b/src/ac/ed/lurg/landuse/WoodUsageReader.java @@ -1,96 +1,96 @@ -package ac.ed.lurg.landuse; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import ac.ed.lurg.country.CompositeCountryManager; -import ac.ed.lurg.country.CountryManager; -import ac.ed.lurg.country.SingleCountry; -import ac.ed.lurg.ModelConfig; -import ac.ed.lurg.country.CompositeCountry; -import ac.ed.lurg.types.WoodType; -import ac.ed.lurg.utils.LazyHashMap; -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 CompositeCountryManager compositeCountryManager; - - public WoodUsageReader(CompositeCountryManager compositeCountryManager) { - this.compositeCountryManager = compositeCountryManager; - } - - public Map<CompositeCountry, Map<WoodType, WoodUsageData>> getWoodUsageData() { - - LazyHashMap<CompositeCountry, Map<WoodType, WoodUsageData>> usageMap = new LazyHashMap<CompositeCountry, Map<WoodType, WoodUsageData>>() { - private static final long serialVersionUID = -3848653213145539313L; - - protected Map<WoodType, WoodUsageData> createValue() { - Map<WoodType, WoodUsageData> countryData = new HashMap<WoodType, WoodUsageData>(); - return countryData; - } - }; - - String filename = ModelConfig.WOOD_NET_IMPORTS_FILE; - try { - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line, countryName, woodTypeName; - double harvest, netImport; - reader.readLine(); // read header - - while ((line = reader.readLine()) != null) { - String[] tokens = line.split(","); - - if (tokens.length < 4) - LogWriter.printlnError("Too few columns in file: " + filename + ", line: " + line); - - countryName = tokens[COUNTRY_COL]; - woodTypeName = tokens[WOOD_TYPE_COL]; - harvest = Double.parseDouble(tokens[HARVEST_COL]) * ModelConfig.WOOD_BIOMASS_CONVERSION_FACTOR; // m3 to MtC-eq - netImport = Double.parseDouble(tokens[NET_IMPORT_COL]) * ModelConfig.WOOD_BIOMASS_CONVERSION_FACTOR; // m3 to MtC-eq - - SingleCountry country = CountryManager.getForName(countryName); - WoodType woodType = WoodType.getForName(woodTypeName); - - if (country == null) { - // LogWriter.printlnWarning("WoodUsageReader can't find single country: " + countryName); - continue; - } - - CompositeCountry cc = compositeCountryManager.getForSingleCountry(country); - - if (cc == null) { - // LogWriter.printlnWarning("WoodUsageReader can't find composite country for: " + country.getCountryName()); - continue; - } - - Map<WoodType, WoodUsageData> countryData = usageMap.lazyGet(cc); - WoodUsageData oldData = countryData.get(woodType); - - // aggregate if multiple countries for cc - if (oldData != null) { - netImport += oldData.getNetImport(); - harvest += oldData.getHarvest(); - } - - WoodUsageData newData = new WoodUsageData(harvest, netImport); - countryData.put(woodType, newData); - } - reader.close(); - - } catch (IOException e) { - LogWriter.printlnError("Failed in reading wood usage data"); - LogWriter.print(e); - } - LogWriter.println("Processed " + filename + ", create " + usageMap.size() + " country wood usage map values"); - - return usageMap; - } -} +package ac.ed.lurg.landuse; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.country.CompositeCountryManager; +import ac.ed.lurg.country.CountryManager; +import ac.ed.lurg.country.SingleCountry; +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.country.CompositeCountry; +import ac.ed.lurg.types.WoodType; +import ac.ed.lurg.utils.LazyHashMap; +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 CompositeCountryManager compositeCountryManager; + + public WoodUsageReader(CompositeCountryManager compositeCountryManager) { + this.compositeCountryManager = compositeCountryManager; + } + + public Map<CompositeCountry, Map<WoodType, WoodUsageData>> getWoodUsageData() { + + LazyHashMap<CompositeCountry, Map<WoodType, WoodUsageData>> usageMap = new LazyHashMap<CompositeCountry, Map<WoodType, WoodUsageData>>() { + private static final long serialVersionUID = -3848653213145539313L; + + protected Map<WoodType, WoodUsageData> createValue() { + Map<WoodType, WoodUsageData> countryData = new HashMap<WoodType, WoodUsageData>(); + return countryData; + } + }; + + String filename = ModelConfig.WOOD_NET_IMPORTS_FILE; + try { + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line, countryName, woodTypeName; + double harvest, netImport; + reader.readLine(); // read header + + while ((line = reader.readLine()) != null) { + String[] tokens = line.split(","); + + if (tokens.length < 4) + LogWriter.printlnError("Too few columns in file: " + filename + ", line: " + line); + + countryName = tokens[COUNTRY_COL]; + woodTypeName = tokens[WOOD_TYPE_COL]; + harvest = Double.parseDouble(tokens[HARVEST_COL]) * ModelConfig.WOOD_BIOMASS_CONVERSION_FACTOR; // m3 to MtC-eq + netImport = Double.parseDouble(tokens[NET_IMPORT_COL]) * ModelConfig.WOOD_BIOMASS_CONVERSION_FACTOR; // m3 to MtC-eq + + SingleCountry country = CountryManager.getForName(countryName); + WoodType woodType = WoodType.getForName(woodTypeName); + + if (country == null) { + // LogWriter.printlnWarning("WoodUsageReader can't find single country: " + countryName); + continue; + } + + CompositeCountry cc = compositeCountryManager.getForSingleCountry(country); + + if (cc == null) { + // LogWriter.printlnWarning("WoodUsageReader can't find composite country for: " + country.getCountryName()); + continue; + } + + Map<WoodType, WoodUsageData> countryData = usageMap.lazyGet(cc); + WoodUsageData oldData = countryData.get(woodType); + + // aggregate if multiple countries for cc + if (oldData != null) { + netImport += oldData.getNetImport(); + harvest += oldData.getHarvest(); + } + + WoodUsageData newData = new WoodUsageData(harvest, netImport, 0); + countryData.put(woodType, newData); + } + reader.close(); + + } catch (IOException e) { + LogWriter.printlnError("Failed in reading wood usage data"); + LogWriter.print(e); + } + LogWriter.println("Processed " + filename + ", create " + usageMap.size() + " country wood usage map values"); + + return usageMap; + } +} diff --git a/src/ac/ed/lurg/output/LandUseOutputer.java b/src/ac/ed/lurg/output/LandUseOutputer.java index 82eed7ea30d5d5841e87e1ca8bc37a94529a406e..ab42c6fc325877d4c40071da70446b15a30a04bc 100644 --- a/src/ac/ed/lurg/output/LandUseOutputer.java +++ b/src/ac/ed/lurg/output/LandUseOutputer.java @@ -1,99 +1,100 @@ -package ac.ed.lurg.output; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Map.Entry; - -import ac.ed.lurg.landuse.Intensity; -import ac.ed.lurg.landuse.LandUseItem; -import ac.ed.lurg.types.CropType; -import ac.ed.lurg.types.LandCoverType; -import ac.ed.lurg.utils.LogWriter; -import ac.sac.raster.RasterKey; -import ac.sac.raster.RasterSet; - -public class LandUseOutputer extends AbstractLandUseOutputer { - - public LandUseOutputer(int year, RasterSet<LandUseItem> landUseRaster) { - super(year, landUseRaster); - } - - @Override - public void writeOutput() { - File outputDir = getOutputDir(year); - BufferedWriter fertWriter = null; - - try { - String landCoverFileName = outputDir.getPath() + File.separator + "LandUse.txt"; - fertWriter = new BufferedWriter(new FileWriter(landCoverFileName, false)); - - StringBuffer sbHeader = new StringBuffer("Lon Lat area suitable protected pa_fraction"); - - for (LandCoverType cover : LandCoverType.values()) { - sbHeader.append(" " + cover.getName()); - } - - for (CropType crop : CropType.getNonMeatTypes()) { - String cropString = crop.getGamsName(); - sbHeader.append(" " + cropString + "_A " + cropString + "_FI " + cropString + "_FQ " + - cropString + "_II " + cropString + "_IQ " + cropString + "_OI " + cropString + "_Y"); - } - fertWriter.write(sbHeader.toString()); - fertWriter.newLine(); - - for (Entry<RasterKey, LandUseItem> entry : landUseRaster.entrySet()) { - RasterKey key = entry.getKey(); - LandUseItem item = entry.getValue(); - - if (item == null) - continue; - - double lon = landUseRaster.getXCoordin(key); - double lat = landUseRaster.getYCoordin(key); - - - StringBuffer sbData = new StringBuffer(String.format("%.2f %.2f", lon, lat)); - - sbData.append(String.format(" %.8f", item.getTotalLandCoverArea())); - sbData.append(String.format(" %.8f", item.getSuitableArea())); - sbData.append(String.format(" %.8f", item.getTotalProtectedArea())); - sbData.append(String.format(" %.8f", item.getTotalProtectedArea()/item.getTotalLandCoverArea())); - - for (LandCoverType cover : LandCoverType.values()) { - sbData.append(String.format(" %.8f", item.getLandCoverArea(cover))); - } - - for (CropType crop : CropType.getNonMeatTypes()) { - double cropFract = item.getCropFraction(crop); - Intensity intensity = item.getIntensity(crop); - double fertI = intensity==null ? 0.0 : intensity.getFertiliserIntensity(); - double fertQ = intensity==null ? 0.0 : intensity.getFertiliserAmount(); - double irrigI = intensity==null ? 0.0 : intensity.getIrrigationIntensity(); - double irrigQ = intensity==null ? 0.0 : intensity.getIrrigationRate(); - double otherI = intensity==null ? 0.0 : intensity.getOtherIntensity(); - double yield = intensity==null ? 0.0 : intensity.getYield(); - sbData.append(String.format(" %.8f %.8f %.8f %.8f %.8f %.8f %.8f", cropFract, fertI, fertQ, irrigI, irrigQ, otherI, yield)); - } - fertWriter.write(sbData.toString()); - fertWriter.newLine(); - } - } - catch (IOException e) { - LogWriter.print(e); - } - finally { - if (fertWriter != null) { - try { - fertWriter.close(); - } - catch (IOException e) { - LogWriter.print(e); - } - } - } - - } - -} +package ac.ed.lurg.output; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Map.Entry; + +import ac.ed.lurg.landuse.Intensity; +import ac.ed.lurg.landuse.LandUseItem; +import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.types.LandProtectionType; +import ac.ed.lurg.utils.LogWriter; +import ac.sac.raster.RasterKey; +import ac.sac.raster.RasterSet; + +public class LandUseOutputer extends AbstractLandUseOutputer { + + public LandUseOutputer(int year, RasterSet<LandUseItem> landUseRaster) { + super(year, landUseRaster); + } + + @Override + public void writeOutput() { + File outputDir = getOutputDir(year); + BufferedWriter fertWriter = null; + + try { + String landCoverFileName = outputDir.getPath() + File.separator + "LandUse.txt"; + fertWriter = new BufferedWriter(new FileWriter(landCoverFileName, false)); + + StringBuffer sbHeader = new StringBuffer("Lon Lat area suitable protected pa_fraction"); + + for (LandCoverType cover : LandCoverType.values()) { + sbHeader.append(" " + cover.getName()); + } + + for (CropType crop : CropType.getNonMeatTypes()) { + String cropString = crop.getGamsName(); + sbHeader.append(" " + cropString + "_A " + cropString + "_FI " + cropString + "_FQ " + + cropString + "_II " + cropString + "_IQ " + cropString + "_OI " + cropString + "_Y"); + } + fertWriter.write(sbHeader.toString()); + fertWriter.newLine(); + + for (Entry<RasterKey, LandUseItem> entry : landUseRaster.entrySet()) { + RasterKey key = entry.getKey(); + LandUseItem item = entry.getValue(); + + if (item == null) + continue; + + double lon = landUseRaster.getXCoordin(key); + double lat = landUseRaster.getYCoordin(key); + + + StringBuffer sbData = new StringBuffer(String.format("%.2f %.2f", lon, lat)); + + sbData.append(String.format(" %.8f", item.getTotalLandCoverArea())); + sbData.append(String.format(" %.8f", item.getSuitableArea())); + sbData.append(String.format(" %.8f", item.getTotalLandCoverArea(LandProtectionType.PROTECTED))); + sbData.append(String.format(" %.8f", item.getTotalLandCoverArea(LandProtectionType.PROTECTED)/item.getTotalLandCoverArea())); + + for (LandCoverType cover : LandCoverType.values()) { + sbData.append(String.format(" %.8f", item.getLandCoverArea(cover))); + } + + for (CropType crop : CropType.getNonMeatTypes()) { + double cropFract = item.getCropFraction(crop); + Intensity intensity = item.getIntensity(crop); + double fertI = intensity==null ? 0.0 : intensity.getFertiliserIntensity(); + double fertQ = intensity==null ? 0.0 : intensity.getFertiliserAmount(); + double irrigI = intensity==null ? 0.0 : intensity.getIrrigationIntensity(); + double irrigQ = intensity==null ? 0.0 : intensity.getIrrigationRate(); + double otherI = intensity==null ? 0.0 : intensity.getOtherIntensity(); + double yield = intensity==null ? 0.0 : intensity.getYield(); + sbData.append(String.format(" %.8f %.8f %.8f %.8f %.8f %.8f %.8f", cropFract, fertI, fertQ, irrigI, irrigQ, otherI, yield)); + } + fertWriter.write(sbData.toString()); + fertWriter.newLine(); + } + } + catch (IOException e) { + LogWriter.print(e); + } + finally { + if (fertWriter != null) { + try { + fertWriter.close(); + } + catch (IOException e) { + LogWriter.print(e); + } + } + } + + } + +} diff --git a/src/ac/ed/lurg/types/LandCoverType.java b/src/ac/ed/lurg/types/LandCoverType.java index 7398c8a2c2f658ed7f264b70ea1c57f434cf00a0..d9aac67874a670c815ea53063c0039a2ef01a919 100644 --- a/src/ac/ed/lurg/types/LandCoverType.java +++ b/src/ac/ed/lurg/types/LandCoverType.java @@ -1,132 +1,132 @@ -package ac.ed.lurg.types; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import ac.ed.lurg.utils.LogWriter; - -public enum LandCoverType { - - TIMBER_FOREST("timberForest", false, true, true, true), - CARBON_FOREST("carbonForest", false, true, true, true), - NATURAL ("natural", true, true, true, false), - CROPLAND ("cropland", false, true, false, false), - PASTURE ("pasture", false, true, false, false), - BARREN ("barren", false, false, false, false), - URBAN("urban", false, false, false, false); - - private String name; - private boolean isProtectable; - private boolean isConvertible; - private boolean isNatural; - private boolean isManagedForest; - - LandCoverType(String name, boolean isProtectable, boolean isConvertible, boolean isNatural, boolean isManagedForest) { - this.name = name; - this.isProtectable = isProtectable; - this.isConvertible = isConvertible; - this.isNatural = isNatural; - this.isManagedForest = isManagedForest; - } - - public String getName() { - return name; - } - - public boolean isProtectable() { - return isProtectable; - } - - public boolean isConvertible() { - return isConvertible; - } - - public boolean isNatural() { - return isNatural; - } - - public boolean isManagedForest() { - return isManagedForest; - } - - private static final Map<String, LandCoverType> nameCache = new HashMap<String, LandCoverType>(); - static { - for (LandCoverType c : values()) { - nameCache.put(c.getName(), c); - - } - } - - public static LandCoverType getForName(String name) { - LandCoverType type = nameCache.get(name); - - if (type == null) - LogWriter.printlnError("Can't find LandCoverType for " + name); - - return type; - } - - - public static Collection<LandCoverType> getConvertibleTypes() { - - Collection<LandCoverType> convertibleTypes = new HashSet<LandCoverType>(); - - for (LandCoverType c : values()) - if (c.isConvertible) - convertibleTypes.add(c); - - return convertibleTypes; - - } - - public static Collection<LandCoverType> getProtectibleTypes() { - - Collection<LandCoverType> protectibleTypes = new HashSet<LandCoverType>(); - - for (LandCoverType c : values()) - if (c.isProtectable) - protectibleTypes.add(c); - - return protectibleTypes; - - } - - public static Collection<LandCoverType> getManagedForestTypes() { - - Collection<LandCoverType> managedForestTypes = new HashSet<LandCoverType>(); - - for (LandCoverType c : values()) - if (c.isManagedForest) - managedForestTypes.add(c); - - return managedForestTypes; - - } - - public static Collection<LandCoverType> getNaturalTypes() { - - Collection<LandCoverType> naturalTypes = new HashSet<LandCoverType>(); - - for (LandCoverType c : values()) - if (c.isNatural) - naturalTypes.add(c); - - return naturalTypes; - - } - - public static Collection<LandCoverType> getAgriculturalTypes() { - - Collection<LandCoverType> agriTypes = new HashSet<LandCoverType>(); - - for (LandCoverType c : values()) - if (!c.isNatural && c.isConvertible) - agriTypes.add(c); - - return agriTypes; - - } -} - +package ac.ed.lurg.types; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import ac.ed.lurg.utils.LogWriter; + +public enum LandCoverType { + + TIMBER_FOREST("timberForest", false, true, true, true), + CARBON_FOREST("carbonForest", false, true, true, true), + NATURAL ("natural", true, true, true, false), + CROPLAND ("cropland", false, true, false, false), + PASTURE ("pasture", false, true, false, false), + BARREN ("barren", false, false, false, false), + URBAN("urban", true, false, false, false); + + private String name; + private boolean isProtectable; + private boolean isConvertible; + private boolean isNatural; + private boolean isManagedForest; + + LandCoverType(String name, boolean isProtectable, boolean isConvertible, boolean isNatural, boolean isManagedForest) { + this.name = name; + this.isProtectable = isProtectable; + this.isConvertible = isConvertible; + this.isNatural = isNatural; + this.isManagedForest = isManagedForest; + } + + public String getName() { + return name; + } + + public boolean isProtectable() { + return isProtectable; + } + + public boolean isConvertible() { + return isConvertible; + } + + public boolean isNatural() { + return isNatural; + } + + public boolean isManagedForest() { + return isManagedForest; + } + + private static final Map<String, LandCoverType> nameCache = new HashMap<String, LandCoverType>(); + static { + for (LandCoverType c : values()) { + nameCache.put(c.getName(), c); + + } + } + + public static LandCoverType getForName(String name) { + LandCoverType type = nameCache.get(name); + + if (type == null) + LogWriter.printlnError("Can't find LandCoverType for " + name); + + return type; + } + + + public static Collection<LandCoverType> getConvertibleTypes() { + + Collection<LandCoverType> convertibleTypes = new HashSet<LandCoverType>(); + + for (LandCoverType c : values()) + if (c.isConvertible) + convertibleTypes.add(c); + + return convertibleTypes; + + } + + public static Collection<LandCoverType> getProtectibleTypes() { + + Collection<LandCoverType> protectibleTypes = new HashSet<LandCoverType>(); + + for (LandCoverType c : values()) + if (c.isProtectable) + protectibleTypes.add(c); + + return protectibleTypes; + + } + + public static Collection<LandCoverType> getManagedForestTypes() { + + Collection<LandCoverType> managedForestTypes = new HashSet<LandCoverType>(); + + for (LandCoverType c : values()) + if (c.isManagedForest) + managedForestTypes.add(c); + + return managedForestTypes; + + } + + public static Collection<LandCoverType> getNaturalTypes() { + + Collection<LandCoverType> naturalTypes = new HashSet<LandCoverType>(); + + for (LandCoverType c : values()) + if (c.isNatural) + naturalTypes.add(c); + + return naturalTypes; + + } + + public static Collection<LandCoverType> getAgriculturalTypes() { + + Collection<LandCoverType> agriTypes = new HashSet<LandCoverType>(); + + for (LandCoverType c : values()) + if (!c.isNatural && c.isConvertible) + agriTypes.add(c); + + return agriTypes; + + } +} +