SoilFileBuilder.java [src/java/d/soils] Revision: default  Date:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package d.soils;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import soils.Horizon;
import soils.utils.SoilUtils;

/**
 *
 * @author sidereus
 */
public class SoilFileBuilder {

  private Map<Double, List<Profile>> strata;
  private double top = 0.0;
  private boolean todo = Boolean.FALSE;


  public SoilFileBuilder(Map<Double, List<Profile>> strata) {
    this.strata = strata;
  }


  public String build() {
    String outLine = "";
    for (Map.Entry<Double, List<Profile>> entry : strata.entrySet()) {
      if (entry.getValue().size() > 1) {
        // compute weighted average
        List<Double> avg = profileDepthWeightedAverage(entry);
        outLine += buildInLine(entry, avg);
      } else {
        List<Double> avg = new ArrayList<>();
        avg.add(1.0);
        outLine += buildInLine(entry, avg);
      }
    }

    return outLine;
  }


  private List<Double> profileDepthWeightedAverage(Map.Entry<Double, List<Profile>> entry) {
    List<Double> weightedAvg = new LinkedList<>();
    double bottom = entry.getKey();
    for (Profile prof : entry.getValue()) {
      double avg = prof.getWeight(top, bottom);
      weightedAvg.add(avg);
    }
    return weightedAvg;
  }


  /*
Column  1 - Minimum depth of soil layer (cm)
Column  2 - Maximum depth of soil layer (cm)
Column  3 - Bulk density of soil layer (g/cm^3)
Column  4 - Field capacity of soil layer, volumetric
Column  5 - Wilting point of soil layer, volumetric
Column  6 - Evaporation coefficient for soil layer (currently not being used)
Column  7 - Percentage of roots in soil layer, these values must sum to 1.0
Column  8 - Fraction of sand in soil layer, 0.0 - 1.0
Column  9 - Fraction of clay in soil layer, 0.0 - 1.0
Column 10 - Organic matter in soil layer, fraction 0.0 - 1.0
Column 11 - Minimum volumetric soil water content below wilting point for soil
            layer, soil water content will not be allowed to drop below this
            value
Column 12 - Saturated hydraulic conductivity of soil layer in centimeters per
            second
Column 13 - pH of soil layer
   */
  private String buildInLine(Map.Entry<Double, List<Profile>> entry, List<Double> avg) {

    double thirdbar = 0.0;
    double fieldCapacity = 0.0;
    double wiltPoint = 0.0;
    double evaporation = 0.0;
    double percentRoots = 0.0;
    double sand = 0.0;
    double clay = 0.0;
    double om = 0.0;
    double waterContent = 0.0;
    double conductivity = 0.0;
    double ph = 0.0;

    if (entry.getValue().size() != avg.size()) {
      String msg = "Avg and entry have different lengths";
      throw new RuntimeException(msg);
    }

    for (int i = 0; i < avg.size(); i++) {
      Horizon horizon = entry.getValue().get(i).getHorizon();
      double tmpavg = avg.get(i);
      if (tmpavg > 1.0) {
        String msg = "Weghted average bigger than 1.0";
        throw new RuntimeException(msg);
      }

      thirdbar += horizon.dbthirdbar_r() * tmpavg;
      fieldCapacity += SoilUtils.getFieldCapacitySWC(horizon) * tmpavg;
      wiltPoint += SoilUtils.getWiltingPointSWC(horizon) * tmpavg;
      evaporation += 0;
      percentRoots += 0;
      sand += horizon.sandtotal_r() / 100 * tmpavg;
      clay += horizon.claytotal_r() / 100 * tmpavg;
      om += horizon.om_r() / 100 * tmpavg;
      conductivity += (horizon.ksat_r() / 10000.0) * tmpavg;
      ph += SoilUtils.getSoilpH(horizon) * tmpavg;
    }

    double toplayer = top;
    double bottomlayer = entry.getKey();

    if (bottomlayer == 2.0) {
      waterContent = 0.09;
      evaporation = 0.8;
      percentRoots = 0.3 * 2 / 10;
    } else if (bottomlayer == 5.0) {
      waterContent = 0.07;
      evaporation = 0.2;
      percentRoots = 0.3 * 3 / 10;
    } else if (bottomlayer == 10.0) {
      waterContent = 0.05;
      percentRoots = 0.3 * 5 / 10;
      todo = Boolean.TRUE;
    } else if (todo) {
      waterContent = 0.02;
      todo = Boolean.FALSE;
    }

    if (bottomlayer > 10.0 & toplayer <= 90.0) {
      double depth = 0.0;
      if (bottomlayer > 90.0) {
        depth = 90 - toplayer;
      } else {
        depth = bottomlayer - toplayer;
      }
      percentRoots = 0.7 * depth / 80;
    }

    top = bottomlayer;

    // tmp fix to avoid waterContent > wiltpoint
    if (waterContent > wiltPoint) {
      if (wiltPoint == 0.0) {
        waterContent = wiltPoint;
      } else if (wiltPoint > 0.01) {
      waterContent = wiltPoint - 0.01;
      } else {
        waterContent = 0.0;
      }
    }

    return space8format(toplayer) // top layer
        + space8format(bottomlayer) // bottom layer
        + space9format(thirdbar) // bulk density
        + space12format(fieldCapacity) // field capacity
        + space12format(wiltPoint) // wilting point
        + space9format(evaporation) // evaporation
        + space9format(percentRoots) // percentage roots
        + space9format(sand) // fraction sand
        + space9format(clay) // fraction clay
        + space11format(om) // organic matter
        + space9format(waterContent) // soil water content
        + space15format(conductivity) // saturated hydraulic conductivity mm/h to cm/s
        + space4format(ph) // pH
        + System.lineSeparator();
  }


  private String space4format(double value) {
    DecimalFormat df1 = new DecimalFormat("0.00");
    return String.format("%1$-4s", df1.format(value));
  }


  private String space8format(double value) {
    DecimalFormat df1 = new DecimalFormat("0.0");
    return String.format("%1$-8s", df1.format(value));
  }


  private String space9format(double value) {
    DecimalFormat df1 = new DecimalFormat("0.00");
    return String.format("%1$-9s", df1.format(value));
  }


  private String space11format(double value) {
    DecimalFormat df1 = new DecimalFormat("0.0000");
    return String.format("%1$-11s", df1.format(value));
  }


  private String space12format(double value) {
    DecimalFormat df1 = new DecimalFormat("0.00000");
    return String.format("%1$-12s", df1.format(value));
  }


  private String space15format(double value) {
    DecimalFormat df1 = new DecimalFormat("0.00000000");
    return String.format("%1$-15s", df1.format(value));
  }

}