Newer
Older
package ac.ed.lurg.forestry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import ac.ed.lurg.ModelConfig;
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 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.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;
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));
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;
}