Skip to content
Snippets Groups Projects
WoodYieldItem.java 3.82 KiB
Newer Older
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.Timestep;
import ac.ed.lurg.landuse.LandCoverTile;
import ac.ed.lurg.landuse.LccKey;
import ac.ed.lurg.types.LandCoverType;
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 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.getTotalConvertibleArea(); // 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++) {
					totalYield += yields[age] * tiles.getConvertibleArea(age);
				double meanYield = totalYield / totalArea;
	public void calcRotationData(Map<LccKey, Double[]> woodYields, Map<LandCoverType, LandCoverTile> landUseTiles, Timestep timestep, double woodPrice) {
		List<Double> levArr = new ArrayList<Double>();
		
		Double[] yields = woodYields.get(new LccKey(LandCoverType.TIMBER_FOREST, LandCoverType.TIMBER_FOREST));
		for (int age = 0; age <= WoodYieldItem.MAX_AGE; 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];
		
		final double OVER_ROTA_AGE_HARVEST_PROP = 0.05;
		LandCoverTile timberTiles = landUseTiles.get(LandCoverType.TIMBER_FOREST);
		double timberForestArea = timberTiles.getTotalConvertibleArea();
		if (timberForestArea == 0) {
			currentRotationHarvest = 0.0;
		} else {
			currentRotationHarvest += timberTiles.getConvertibleArea(optimalRotation) * yieldAtRotation;
			timberTiles.resetAreaForAge(optimalRotation); // WARNING: side effect
			
			for (int age=optimalRotation+1; age<=LandCoverTile.getMaxAgeBin(); age++) { // TODO better way of dealing with stands over rotation age?
				double areaHarvested = timberTiles.getConvertibleArea(age) * OVER_ROTA_AGE_HARVEST_PROP;
				currentRotationHarvest += areaHarvested * yields[age];
				timberTiles.resetAreaForAge(age, areaHarvested); 
	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;
	}