SoilFileBuilder2.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 csip.api.server.ServiceException;
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 SoilFileBuilder2 {

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


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


  public String build() throws Exception {
    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) throws Exception {

    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()) {
      throw new ServiceException("Avg and entry have different lengths");
    }

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

      thirdbar += horizon.dbthirdbar_r() * tmpavg;
      fieldCapacity += horizon.fieldCapacitySWC() * tmpavg; //testDouble(SoilUtils.getFieldCapacitySWC(horizon), " Field Capacity SWC ")  * tmpavg;
      wiltPoint += horizon.wiltingPointSWC() * tmpavg; //testDouble(SoilUtils.getWiltingPointSWC(horizon), "Wiltng Point SWC") * tmpavg;
      evaporation += 0;
      percentRoots += 0;
      sand += testDouble(horizon.sandtotal_r(), "sandtotal_r") / 100 * tmpavg;
      clay += testDouble(horizon.claytotal_r(), "claytotal_r") / 100 * tmpavg;
      om += testDouble(horizon.om_r(), "om_r") / 100 * tmpavg;
      conductivity += (testDouble(horizon.ksat_r(), "ksat_r") / 10000.0) * tmpavg;
      ph += horizon.soilPh() * tmpavg; //testDouble(SoilUtils.getSoilpH(horizon), "soil pH") * tmpavg;
    }

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

    if (bottomlayer == 2.0) {
      waterContent = 0.09;
      evaporation = 0.8;
//      percentRoots = 0.027; // was 0.3 * 2 / 10;
      percentRoots = 0.3 * 2 / 10;
    } else if (bottomlayer == 5.0) {
      waterContent = 0.07;
      evaporation = 0.2;
      percentRoots = 0.040; // was 0.3 * 3 / 10;
//      percentRoots = 0.3 * 3 / 10;
    } else if (bottomlayer == 10.0) {
      waterContent = 0.05;
//      percentRoots = 0.067; // was 0.3 * 5 / 10;
      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;
    }

//    if (bottomlayer == 20.0) {
//      percentRoots = 0.133;
//    } else if (bottomlayer == 30.0) {
//      percentRoots = 0.133;
//    } else if (bottomlayer == 45.0) {
//      percentRoots = 0.1;
//    } else if (bottomlayer == 60.0) {
//      percentRoots = 0.1;
//    } else if (bottomlayer == 75.0) {
//      percentRoots = 0.1;
//    } else if (bottomlayer == 90.0) {
//      percentRoots = 0.1;
//    } else if (bottomlayer == 105.0) {
//      percentRoots = 0.033;
//    } else if (bottomlayer == 120.0) {
//      percentRoots = 0.033;
//    } else if (bottomlayer == 150.0) {
//      percentRoots = 0.067;
//    } else if (bottomlayer == 180.0) {
//      percentRoots = 0.067;
//    }
    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;
      }
    } else if (waterContent == wiltPoint && waterContent > 0) {
      waterContent = wiltPoint - 0.01;
    }

    return space8format(toplayer) // top layer
        + space8format(bottomlayer) // bottom layer
        + space9format(thirdbar) // bulk density
        + space12format(fieldCapacity)//getFieldCapacity(toplayer, calibration, fieldCapacity) // field capacity
        + space12format(wiltPoint)//getWiltingPoint(toplayer, calibration, wiltPoint) // wilting point
        + space9format(evaporation) // evaporation
        + space9format3(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 < 6.5) && (toplayer < 30)) ? 6.5 : ph)) // pH
        + System.lineSeparator();
  }


  private String getFieldCapacity(double toplayer, boolean calibration, double fieldCapacity) {
    if (calibration) {
      if (toplayer == 0.0) {
        return "${daycent.fieldCapacity1}  ";
      } else if (toplayer == 2.0) {
        return "${daycent.fieldCapacity2}  ";
      } else if (toplayer == 5.0) {
        return "${daycent.fieldCapacity3}  ";
      } else if (toplayer == 10.0) {
        return "${daycent.fieldCapacity4}  ";
      } else if (toplayer == 20.0) {
        return "${daycent.fieldCapacity5}  ";
      } else if (toplayer == 30.0) {
        return "${daycent.fieldCapacity6}  ";
      } else if (toplayer == 45.0) {
        return "${daycent.fieldCapacity7}  ";
      } else if (toplayer == 60.0) {
        return "${daycent.fieldCapacity8}  ";
      } else if (toplayer == 75.0) {
        return "${daycent.fieldCapacity9}  ";
      } else if (toplayer == 90.0) {
        return "${daycent.fieldCapacity10}  ";
      } else {
        return space12format(fieldCapacity);
      }
    } else {
      return space12format(fieldCapacity);
    }
  }


  private String getWiltingPoint(double toplayer, boolean calibration, double wiltingPoint) {
    if (calibration) {
      if (toplayer == 0.0) {
        return "${daycent.wiltingPoint1}  ";
      } else if (toplayer == 2.0) {
        return "${daycent.wiltingPoint2}  ";
      } else if (toplayer == 5.0) {
        return "${daycent.wiltingPoint3}  ";
      } else if (toplayer == 10.0) {
        return "${daycent.wiltingPoint4}  ";
      } else if (toplayer == 20.0) {
        return "${daycent.wiltingPoint5}  ";
      } else if (toplayer == 30.0) {
        return "${daycent.wiltingPoint6}  ";
      } else if (toplayer == 45.0) {
        return "${daycent.wiltingPoint7}  ";
      } else if (toplayer == 60.0) {
        return "${daycent.wiltingPoint8}  ";
      } else if (toplayer == 75.0) {
        return "${daycent.wiltingPoint9}  ";
      } else if (toplayer == 90.0) {
        return "${daycent.wiltingPoint10}  ";
      } else {
        return space12format(wiltingPoint);
      }
    } else {
      return space12format(wiltingPoint);
    }
  }


  private double testDouble(double value, String field) throws Exception {
    if (Double.isNaN(value)) {
      throw new ServiceException("The value passed was NaN, indicating that for this field, " + field + ", of the SDM database there was missing data.  Cannot continue building this SoilIN line.");
    }

    return value;
  }


  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 space9format3(double value) {
    DecimalFormat df1 = new DecimalFormat("0.000");
    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));
  }

}