Skip to content
Snippets Groups Projects
ModelMain.java 18.9 KiB
Newer Older
package ac.ed.lurg;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.HashMap;
Peter Alexander's avatar
Peter Alexander committed
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import ac.ed.lurg.country.CompositeCountry;
import ac.ed.lurg.country.CompositeCountryManager;
import ac.ed.lurg.country.CountryAgent;
import ac.ed.lurg.country.CountryBoundaryRaster;
import ac.ed.lurg.country.CountryBoundaryReader;
import ac.ed.lurg.country.GlobalPrice;
R0slyn's avatar
R0slyn committed
import ac.ed.lurg.country.TradeManager;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.country.gams.GamsRasterOutput;
import ac.ed.lurg.demand.BaseConsumpManager;
import ac.ed.lurg.demand.DemandManager;
import ac.ed.lurg.landuse.CropUsageData;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.landuse.CropUsageReader;
import ac.ed.lurg.landuse.IrrigationConstraintReader;
import ac.ed.lurg.landuse.IrrigationItem;
import ac.ed.lurg.landuse.IrrigationMaxAmountReader;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.landuse.IrrigiationCostReader;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.landuse.LandCoverItem;
import ac.ed.lurg.landuse.LandCoverReader;
import ac.ed.lurg.landuse.LandUseItem;
import ac.ed.lurg.landuse.LandUseReader;
import ac.ed.lurg.landuse.ProtectedAreasReader;
import ac.ed.lurg.output.LandUseOutputer;
import ac.ed.lurg.output.LpjgOutputer;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.types.CommodityType;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.types.CropToDoubleMap;
Peter Alexander's avatar
Peter Alexander committed
import ac.ed.lurg.types.CropType;
import ac.ed.lurg.types.LandCoverType;
import ac.ed.lurg.types.ModelFitType;
import ac.ed.lurg.utils.LogWriter;
import ac.ed.lurg.yield.LPJYieldResponseMapReader;
import ac.ed.lurg.yield.YieldRaster;
import ac.ed.lurg.yield.YieldResponsesItem;
import ac.sac.raster.IntegerRasterItem;
import ac.sac.raster.InterpolatingRasterSet;
Peter Alexander's avatar
Peter Alexander committed
import ac.sac.raster.RasterHeaderDetails;
import ac.sac.raster.RasterKey;
import ac.sac.raster.RasterOutputer;
Peter Alexander's avatar
Peter Alexander committed
import ac.sac.raster.RasterSet;

public class ModelMain {

	private Collection<CountryAgent> countryAgents;
	private CountryBoundaryRaster countryBoundaryRaster;
Peter Alexander's avatar
Peter Alexander committed
	private DemandManager demandManager;
R0slyn's avatar
R0slyn committed
	private TradeManager tradeManager;
	private CompositeCountryManager compositeCountryManager;
Peter Alexander's avatar
Peter Alexander committed
	private RasterHeaderDetails desiredProjection;
Peter Alexander's avatar
Peter Alexander committed

	private Map<CropType, GlobalPrice> prevWorldPrices;
	private RasterSet<LandUseItem> prevLandUseRaster;
	private RasterSet<IrrigationItem> currentIrrigationData;
	public static void main(String[] args)  {
		ModelMain theModel = new ModelMain();
		theModel.setup();
		theModel.run();
	}
Peter Alexander's avatar
Peter Alexander committed

	/* setup models, reading inputs, etc. */
	private void setup() {
		desiredProjection = RasterHeaderDetails.getGlobalHeaderFromCellSize(ModelConfig.CELL_SIZE_X, ModelConfig.CELL_SIZE_Y, "999");
		BaseConsumpManager baseConsumpManager = new BaseConsumpManager();
		compositeCountryManager = new CompositeCountryManager(baseConsumpManager);
		demandManager = new DemandManager(ModelFitType.LOGISTIC, ModelConfig.SSP_SCENARIO, baseConsumpManager, compositeCountryManager);
R0slyn's avatar
R0slyn committed
		tradeManager = new TradeManager(compositeCountryManager);
		currentIrrigationData = getFixedIrrigationData();
				
		countryBoundaryRaster = getCountryBoundaryRaster();
		countryAgents = createCountryAgents(compositeCountryManager.getAll());
Peter Alexander's avatar
Peter Alexander committed
		// in first timestep we don't have this info, but ok as constrained to import/export specified amount
		prevWorldPrices = new HashMap<CropType, GlobalPrice>();  
Peter Alexander's avatar
Peter Alexander committed
		prevWorldPrices.put(CropType.WHEAT, GlobalPrice.createInitial(0.15));
		prevWorldPrices.put(CropType.MAIZE, GlobalPrice.createInitial(0.15));
		prevWorldPrices.put(CropType.RICE, GlobalPrice.createInitial(0.4));
		prevWorldPrices.put(CropType.OILCROPS, GlobalPrice.createInitial(0.4));
		prevWorldPrices.put(CropType.PULSES, GlobalPrice.createInitial(0.2));
		prevWorldPrices.put(CropType.STARCHY_ROOTS, GlobalPrice.createInitial(0.1));
		prevWorldPrices.put(CropType.MEAT, GlobalPrice.createInitial(0.2));
Peter Alexander's avatar
Peter Alexander committed
	}
Peter Alexander's avatar
Peter Alexander committed
	/* 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) {
		LogWriter.println(timestep.toString());
		YieldRaster yieldSurfaces = getYieldSurfaces(timestep);  // this will wait for the marker file from LPJ if configured to do so
		RasterSet<IrrigationItem> allIrrigData = getUpdateIrrigationData(timestep, yieldSurfaces);
		YieldResponsesItem yresp = yieldSurfaces.getFromCoordinates(-90.5, 45.5);
		LogWriter.printlnError("Test key: " + yresp.getYieldMax(CropType.MAIZE)  + ", " + yresp.getYieldFertOnly(CropType.MAIZE) + ", " + yresp.getYieldIrrigOnly(CropType.MAIZE) + ", " + yresp.getYieldNone(CropType.MAIZE));
		CropToDoubleMap totalImportCommodities = new CropToDoubleMap();
		CropToDoubleMap totalExportCommodities = new CropToDoubleMap();
		RasterSet<LandUseItem> globalLandUseRaster = new RasterSet<LandUseItem>(desiredProjection);
		RasterSet<IntegerRasterItem> globalLocationIdRaster = new RasterSet<IntegerRasterItem>(desiredProjection);
Peter Alexander's avatar
Peter Alexander committed
		for (CountryAgent ca : countryAgents) {
Peter Alexander's avatar
Peter Alexander committed
			LogWriter.println("Country " + ca.getCountry());
			Collection<RasterKey> countryKeys = countryBoundaryRaster.getKeysFor(ca.getCountry());
Peter Alexander's avatar
Peter Alexander committed
			YieldRaster countryYieldSurfaces = yieldSurfaces.createSubsetForKeys(countryKeys);
			RasterSet<IrrigationItem> irrigData = allIrrigData.createSubsetForKeys(countryKeys);
			
			GamsRasterOutput result = null;
Peter Alexander's avatar
Peter Alexander committed
			// do the optimization
				result = ca.determineProduction(timestep, countryYieldSurfaces, irrigData, prevWorldPrices);
			}
			catch (Exception e) {
				LogWriter.printlnError("Exception processing " + ca.getCountry() + " will continue with other countries");	
				LogWriter.print(e);
Peter Alexander's avatar
Peter Alexander committed
			if (result == null) {
				LogWriter.printlnError("No results for " + ca.getCountry());
				continue;
			}
			
			// some hacky code for debug purposes that keeps each gams gdx file
			if (ca.getCountry().getName().equals("Spain")) {
				try {
					Files.copy(
						FileSystems.getDefault().getPath("/Users/peteralexander/Documents/R_Workspace/UNPLUM/temp/GamsTmp/_gams_java_gdb1.gdx"),
						FileSystems.getDefault().getPath("/Users/peteralexander/Documents/R_Workspace/UNPLUM/temp/GamsTmp/" + timestep.getTimestep() + ".gdx")
						, StandardCopyOption.REPLACE_EXISTING);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} 

			// update global rasters
			globalLandUseRaster.putAll(result.getLandUses());
			globalLocationIdRaster.putAll(result.getLocationIdRaster());
			// Get values for world input costs
			Map<CropType, CropUsageData> cropUsage = result.getCropUsageData();

			for (Entry<CropType, CropUsageData> entry : cropUsage.entrySet()) {
				CropType c = entry.getKey();
				double countryNetImports = entry.getValue().getNetImports();
				if (countryNetImports > 0)
					totalImportCommodities.incrementValue(c, countryNetImports);
				else
					totalExportCommodities.incrementValue(c, -countryNetImports);
Peter Alexander's avatar
Peter Alexander committed
			}
Peter Alexander's avatar
Peter Alexander committed
		}
		// Look at trade balance and adjust appropriately
		for (CropType crop : CropType.getImportedTypes()) {
			GlobalPrice prevPrice = prevWorldPrices.get(crop);

			if (!totalImportCommodities.containsKey(crop) || !totalExportCommodities.containsKey(crop)) {
				LogWriter.printlnError(String.format("Price for %s not updated (still %s), as no imports or no exports for it", crop.getGamsName(), prevPrice));
			double imports = totalImportCommodities.get(crop);
			double exports = totalExportCommodities.get(crop) * (1.0-ModelConfig.TRANSPORT_LOSSES);
			GlobalPrice adjustedPrice = prevPrice.createWithUpdatedMarketPrices(imports, exports, (ModelConfig.MARKET_ADJ_PRICE));
			LogWriter.println(String.format("Price for %s updated from %s (imports amount %.0f, exports amount %.0f) to %s ", crop.getGamsName(), prevPrice, imports, exports, adjustedPrice));
			prevWorldPrices.put(crop, adjustedPrice);
		}
		// output results
		outputTimestepResults(timestep, globalLandUseRaster, globalLocationIdRaster, yieldSurfaces);
		// keep last to allow interpolation
		prevLandUseRaster = globalLandUseRaster;

	private BufferedWriter getFileWriter(Timestep timestep, String file, String columnHeadings) throws IOException {
		if (timestep.isInitialTimestep()) {
			FileWriter fstream = new FileWriter(file,false);
			BufferedWriter outputFile = new BufferedWriter(fstream);
			outputFile.write(columnHeadings);
			outputFile.newLine();
			return outputFile;
		}
		else {
			FileWriter fstream = new FileWriter(file,true);
			return new BufferedWriter(fstream);
		}
	}
	private void writeLandCoverFile(Timestep timestep, RasterSet<LandUseItem> landUseRaster) {
			StringBuffer sbHeadings = new StringBuffer("Year, Cropland (Mha), Pasture (Mha), Natural (Mha), Fert crop (Mt), Fert pasture (Mt), Irrig crop (km3), Irrig pasture (km3)");
			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.LAND_COVER_OUTPUT_FILE, sbHeadings.toString());

			StringBuffer sbData = new StringBuffer();
			sbData.append(String.format("%d, %.1f, %.1f, %.1f", 
					timestep.getYear(), 
					LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.CROPLAND),
					LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.PASTURE),
					LandUseItem.getTotalLandCover(landUseRaster.values(), LandCoverType.OTHER_NATURAL)));
			sbData.append(String.format(", %.1f", LandUseItem.getFertiliserTotal(landUseRaster.values(), CropType.getCropsLessPasture())/1000));
			sbData.append(String.format(", %.1f", LandUseItem.getFertiliserTotal(landUseRaster.values(), CropType.PASTURE)/1000));
			sbData.append(String.format(", %.1f", LandUseItem.getIrrigationTotal(landUseRaster.values(), CropType.getCropsLessPasture())));
			sbData.append(String.format(", %.1f", LandUseItem.getIrrigationTotal(landUseRaster.values(), CropType.PASTURE)));
			outputFile.write(sbData.toString());
			outputFile.newLine();
			outputFile.close();
		}
		catch (IOException e) {
			LogWriter.print(e);
Peter Alexander's avatar
Peter Alexander committed
	}
	
	private void writeGlobalMarketFile(Timestep timestep) {
		try {
			StringBuffer sbHeadings = new StringBuffer("Year,Crop,Imports (Mt),Exports (Mt),New export price");
			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.PRICES_OUTPUT_FILE, sbHeadings.toString());

			for (CropType crop : CropType.getImportedTypes() ) {
				StringBuffer sbData = new StringBuffer();
				sbData.append(String.format("%d,%s", timestep.getYear(), crop.getGamsName()));
				sbData.append(String.format(",%.1f,%.1f", prevWorldPrices.get(crop).getImportAmount(), prevWorldPrices.get(crop).getExportAmount()));
				sbData.append(String.format(",%.3f", prevWorldPrices.get(crop).getExportPrice()));
				outputFile.write(sbData.toString());
				outputFile.newLine();
			}
			
			outputFile.close();
		}
		catch (IOException e) {
			LogWriter.print(e);
		}
	}
Peter Alexander's avatar
Peter Alexander committed
	
	private void writeDemandFile(Timestep timestep) {
		try {
			StringBuffer sbHeadings = new StringBuffer("Year,Commodity,Amount (Mt)");
Peter Alexander's avatar
Peter Alexander committed
			BufferedWriter outputFile = getFileWriter(timestep, ModelConfig.DEMAND_OUTPUT_FILE, sbHeadings.toString());
			
			for (CommodityType comm : CommodityType.getAllItems() ) {
				double demandAmount = 0;
				
				for (CountryAgent country : countryAgents) {
					Double d = country.getCurrentProjectedDemand().get(comm);
					if (d != null)
						demandAmount += d.doubleValue();
				}
				StringBuffer sbData = new StringBuffer();
				sbData.append(String.format("%d,%s", timestep.getYear(), comm.getGamsName()));
				sbData.append(String.format(",%.1f", demandAmount));
Peter Alexander's avatar
Peter Alexander committed
				
				outputFile.write(sbData.toString());
				outputFile.newLine();
			}
			outputFile.close();
		}
		catch (IOException e) {
			LogWriter.print(e);
		}
	}

	private void outputTimestepResults(Timestep timestep, RasterSet<LandUseItem> landUseRaster, RasterSet<IntegerRasterItem> locationIdRaster, YieldRaster yieldSurfaces) {
		writeLandCoverFile(timestep, landUseRaster);
		writeGlobalMarketFile(timestep);
Peter Alexander's avatar
Peter Alexander committed
		writeDemandFile(timestep);
		if (ModelConfig.OUTPUT_FOR_LPJG) {
			for (int outputYear : timestep.getYearsFromLast()) {
				LogWriter.printlnError("Outputing Year: " + outputYear);
				RasterSet<LandUseItem> landUseToOutput = null;
				if (outputYear == timestep.getYear()) {
					landUseToOutput = landUseRaster;
				else if (ModelConfig.INTERPOLATE_OUTPUT_YEARS) {
					InterpolatingRasterSet<LandUseItem> intermediateLandUse = new InterpolatingRasterSet<LandUseItem>(landUseRaster.getHeaderDetails()) {
						private static final long serialVersionUID = 1306045141011047760L;
						protected LandUseItem createRasterData() {
							return new LandUseItem();
					intermediateLandUse.setup(prevLandUseRaster, landUseRaster, timestep.getPreviousTimestep().getYear(), timestep.getYear(), outputYear);
					landUseToOutput = intermediateLandUse;
				if (landUseToOutput != null) {
					LpjgOutputer lpjOutputer = new LpjgOutputer(outputYear, landUseToOutput, yieldSurfaces);
					lpjOutputer.writeOutput();
				}
		
		LogWriter.printlnError("Outputing land uses Year: " + timestep.getYear());
		LandUseOutputer landuseOutputer = new LandUseOutputer(timestep.getYear(), landUseRaster);
		landuseOutputer.writeOutput();
		outputLandCover(timestep.getYear(), landUseRaster, LandCoverType.CROPLAND);
		outputLandCover(timestep.getYear(), landUseRaster, LandCoverType.PASTURE); 
Peter Alexander's avatar
Peter Alexander committed
	}

	private void outputLandCover(int year, RasterSet<LandUseItem> landUseRaster, final LandCoverType lcType) {
		new RasterOutputer<LandUseItem>(landUseRaster, lcType.getName() + "Area" + year) {
			@Override
			public Double getValue(RasterKey location) {
				LandUseItem item = results.get(location);
				if (item == null)
Peter Alexander's avatar
Peter Alexander committed
				return item.getLandCoverFract(lcType);
	public CountryBoundaryRaster getCountryBoundaryRaster() {
		CountryBoundaryRaster countryBoundaries = new CountryBoundaryRaster(desiredProjection, compositeCountryManager);
Peter Alexander's avatar
Peter Alexander committed
		CountryBoundaryReader countryReader = new CountryBoundaryReader(countryBoundaries);
		countryReader.getRasterDataFromFile(ModelConfig.COUNTRY_BOUNDARY_FILE);
		return countryBoundaries;
Peter Alexander's avatar
Peter Alexander committed

	public Collection<CountryAgent> createCountryAgents(Collection<CompositeCountry> countryGrouping) {
Peter Alexander's avatar
Peter Alexander committed
		Collection<CountryAgent> countryAgents = new HashSet<CountryAgent>();

		RasterSet<LandUseItem> initLU = null;
		if (ModelConfig.IS_CALIBRATION_RUN) {
			initLU = getInitialLandCover();
		}
		else {
			initLU = getInitialLandUse(); // read in all land use data saved from calibrations run
		}
Peter Alexander's avatar
Peter Alexander committed
		
		Map<CompositeCountry, Map<CropType, CropUsageData>> cropUsageDataMap = new CropUsageReader(compositeCountryManager).getCommodityData();
		for (CompositeCountry cc : countryGrouping) {
Peter Alexander's avatar
Peter Alexander committed
			// DEBUG code
			if (ModelConfig.DEBUG_LIMIT_COUNTRIES) {
				if (!(cc.getName().equals(ModelConfig.DEBUG_COUNTRY_NAME)))
					continue;
			List<RasterKey> keys = countryBoundaryRaster.getKeysFor(cc);
			Map<CropType, CropUsageData> countryCommodityData = cropUsageDataMap.get(cc);
R0slyn's avatar
R0slyn committed
			Map<CropType, Double> countryTradeBarriers =  tradeManager.getTradeBarriers(cc);
		
Peter Alexander's avatar
Peter Alexander committed
			if (countryCommodityData == null) {
				LogWriter.printlnError("No commodities data for " + cc + ", so skipping");
Peter Alexander's avatar
Peter Alexander committed
			}
			else {
				RasterSet<LandUseItem> initCountryLandUse = initLU.createSubsetForKeys(keys);
				
				if (initCountryLandUse.size() == 0) {
					LogWriter.printlnError("No initial land use for " +cc + ", so skipping");
					continue;
				}

				CountryAgent ca = new CountryAgent(demandManager, cc, initCountryLandUse, countryCommodityData, countryTradeBarriers);
Peter Alexander's avatar
Peter Alexander committed
				countryAgents.add(ca);
				LogWriter.println("Creating country agent for: " + cc );
Peter Alexander's avatar
Peter Alexander committed
		}

		return countryAgents;
	
	/** This is if we are starting from previously saved state (for example, from calibration runs) */
	private RasterSet<LandUseItem> getInitialLandUse() {
		RasterSet<LandUseItem> initLU = new RasterSet<LandUseItem>(desiredProjection) {
			private static final long serialVersionUID = 1317102740312961042L;
			protected LandUseItem createRasterData() {
				return new LandUseItem();
			}
		};
		new LandUseReader(initLU).getRasterDataFromFile(ModelConfig.INITAL_LAND_USE_FILE);
		return initLU;
	} 

	/** 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> getInitialLandCover() {
		RasterSet<LandCoverItem> initialLC = new RasterSet<LandCoverItem>(desiredProjection) {
			private static final long serialVersionUID = 4642550777741425501L;
			protected LandCoverItem createRasterData() {
				return new LandCoverItem();
			}
		new ProtectedAreasReader(initialLC).getRasterDataFromFile(ModelConfig.PROTECTED_AREAS_FILE);
		new LandCoverReader(initialLC).getRasterDataFromFile(ModelConfig.INITAL_LAND_COVER_FILE);
		
		/*	new RasterOutputer<LandCoverItem>(initLC, "InitialCropland") {
			@Override
			public Double getValue(RasterKey location) {
				LandCoverItem item = results.get(location);
				if (item == null)
					return null;
				return item.getLandCoverFract(LandCoverType.CROPLAND);
			}
		}.writeOutput(); */

		RasterSet<LandUseItem> landUseRaster = new RasterSet<LandUseItem>(initialLC.getHeaderDetails());
		
		for (Map.Entry<RasterKey, LandCoverItem> entry : initialLC.entrySet()) {
			LandUseItem areasItem = new LandUseItem();
			RasterKey key = entry.getKey();
			LandCoverItem landCover = entry.getValue();
			areasItem.setLandCoverAreas(landCover);
			areasItem.setCropFraction(CropType.WHEAT, 0.5); // random start, better if we could get data, but free substitution between crops so not critical
			areasItem.setCropFraction(CropType.MAIZE, 0.5);
			areasItem.setProtectedArea(landCover.getProtectedArea());
			landUseRaster.put(key, areasItem);
		}
		return landUseRaster;
Peter Alexander's avatar
Peter Alexander committed

	private YieldRaster getYieldSurfaces(Timestep timestep) {
		LPJYieldResponseMapReader yieldReader = new LPJYieldResponseMapReader(desiredProjection);
		return yieldReader.getRasterData(timestep); 
Peter Alexander's avatar
Peter Alexander committed
	}
	/** Get irrigation data components that don't change over time */
	private RasterSet<IrrigationItem> getFixedIrrigationData() {	
		RasterSet<IrrigationItem> irigData = new RasterSet<IrrigationItem>(desiredProjection) {
Peter Alexander's avatar
Peter Alexander committed
			private static final long serialVersionUID = 8393130687550888654L;
			protected IrrigationItem createRasterData() {
				return new IrrigationItem();
Peter Alexander's avatar
Peter Alexander committed
			}
		};
		new IrrigiationCostReader(irigData).getRasterDataFromFile(ModelConfig.IRRIGATION_COST_FILE);
		new IrrigationConstraintReader(irigData).getRasterDataFromFile(ModelConfig.IRRIGATION_CONSTRAINT_FILE);
	
	private RasterSet<IrrigationItem> getUpdateIrrigationData(Timestep timestep, YieldRaster yieldSurfaces) {		
		String rootDir = timestep.getYearSubDir(ModelConfig.YIELD_DIR);
		new IrrigationMaxAmountReader(currentIrrigationData, yieldSurfaces).getRasterDataFromFile(rootDir + File.separator + ModelConfig.IRRIG_MAX_WATER_FILENAME);
		return currentIrrigationData;
	}
Peter Alexander's avatar
Peter Alexander committed
}