WEPSModelArchive.java [tools/MetaModelTools/src/models] Revision: ec5f4cade4553a8341e1cd241b111bfdb77a87a8  Date: Fri Jan 10 10:59:55 MST 2020
/*
 * 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 models;

import csip.ServiceException;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import data.interpretors.CligenData;
import data.interpretors.IFCFile;
import data.interpretors.WindGenData;
import java.io.BufferedWriter;
import java.io.File;
import java.nio.file.Files;
import java.util.logging.Level;
import java.util.logging.Logger;
import nodes.WEPSManagement;
import org.codehaus.jettison.json.JSONArray;
import parsers.ParserException;
import utils.Urls;

/**
 *
 * @author <a href="mailto:shaun.case@colostate.edu">Shaun Case</a>
 */
public class WEPSModelArchive extends ModelArchive {

  private IFCFile ifcFile;
  private WindGenData windData;
  private CligenData cligenData;
  private String stdErrorFile;
  private String stdOutFile;
  private WEPSManagement management;
  private boolean possibleBadModelRun = false;
  private boolean badModelRun = false;
  private String badModelMessage = "";
  private WEPSMetaData wepsMetaData;

  public WEPSModelArchive(String suid, String ctime, String etime, String service, String status, String req_ip, String filename) {
    super(suid, ctime, etime, service, status, req_ip, filename);
  }

  public WEPSModelArchive(String suid, String ctime, String etime, String service, String status, String req_ip, String filename, byte[] fileData) throws IOException, JSONException {
    super(suid, ctime, etime, service, status, req_ip, filename, fileData);
    setFileDataEx(fileData);
  }

  public WEPSModelArchive(JSONObject metaData) throws JSONException {
    super(metaData);
  }

  public WEPSModelArchive(JSONObject metaData, byte[] fileData) throws JSONException, IOException {
    super(metaData);
    setFileDataEx(fileData);
  }

  @Override
  public final void setFileDataEx(byte[] fileData) throws IOException, JSONException {
    super.setFileDataEx(fileData);

    try {
      stdErrorFile = getStdErrorFile(fileData);
    } catch (IOException ex) {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += ex.getMessage();
    }
    if (null != stdErrorFile) {
      possibleBadModelRun = stdErrorFile.contains("IEEE_UNDERFLOW_FLAG") || stdErrorFile.contains("IEEE_DENORMAL");

      if (possibleBadModelRun) {
        badModelMessage += "##\nThe WEPS model executable stderr file contained: ";
        badModelMessage += ((stdErrorFile.contains("IEEE_UNDERFLOW_FLAG")) ? "\nIEEE_UNDERFLOW_FLAG, Meaning that some values were rounded to zero because they were too small for the FORTRAN code to interpret." : "");
        if (badModelMessage.contains("IEEE")) {
          badModelMessage += "\n AND ";
        }
        badModelMessage += ((stdErrorFile.contains("IEEE_DENORMAL")) ? "\nIEEE_DENORMAL, Meaning that there are denormal numbers generated when running the code." : "");
        badModelMessage += "\n This may be a hint about numerical problems in the model FORTRAN code, but it is not an error per se. Probably, the program finished successfully, but some result values may be suspect.";
      }
    }

//        try {
//            management = getManagementFile(fileData);
//        } catch (IOException ex) {
//            badModelRun = true;
//            badModelMessage += "##\n";
//            badModelMessage += ex.getMessage();
//        }
    try {
      windData = getWindDataFile(fileData);
    } catch (IOException ex) {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += ex.getMessage();
    }

    if (windData.badWindData()) {
      badModelMessage += "##\nThe Wind data associated with this model run has some qualifying messages: \n" + windData.windDataMessages();
    }

    try {
      cligenData = getClimateDataFile(fileData);
    } catch (IOException ex) {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += ex.getMessage();
    }

    if (cligenData.badClimateData()) {
      badModelMessage += "##\nThe Cligen data associated with this model run has some qualifying messages: \n" + cligenData.cligenDataMessages();
    }

    try {
      stdOutFile = getStdOutFile(fileData);
    } catch (IOException ex) {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += ex.getMessage();
    }

    try {
      ifcFile = getIFCFile(fileData);
    } catch (IOException ex) {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += ex.getMessage();
    }

    if (!badModelRun && badModelMessage.isEmpty()) {
      calcWEPSMetaData();
    }
  }

  public boolean questionableResults() {
    return possibleBadModelRun;
  }

  public boolean badModelRun() {
    return badModelRun;
  }

  public String badModelMessage() {
    return badModelMessage;
  }

  public IFCFile iFCFile() {
    return ifcFile;
  }

  public WindGenData windData() {
    return windData;
  }

  public CligenData cligenData() {
    return cligenData;
  }

  public String stdErrorFile() {
    return stdErrorFile;
  }

  public String stdOutFile() {
    return stdOutFile;
  }

  public WEPSMetaData getWEPSMetaData() {
    return wepsMetaData;
  }

  public void calcWEPSMetaData() {
    if (null == wepsMetaData) {
      wepsMetaData = new WEPSMetaData();
    }

    wepsMetaData.suid(this.suid);
    wepsMetaData.cokey(getOriginalRequest("soil"));
    wepsMetaData.longitude(Double.parseDouble(getOriginalRequest("longitude")));
    wepsMetaData.latitude(Double.parseDouble(getOriginalRequest("latitude")));
    wepsMetaData.annualPrecip(cligenData.annualAvgPrecip());
    wepsMetaData.windEnergy(windData.simulationAverage());
    wepsMetaData.componentName(ifcFile.componentName);
    wepsMetaData.fractionSand(ifcFile.fractionSand);
    wepsMetaData.fractionSilt(ifcFile.fractionSilt);
    wepsMetaData.fractionClay(ifcFile.fractionClay);
    wepsMetaData.crustStability(ifcFile.crustStability);
    wepsMetaData.surfRockFrag(ifcFile.surfaceFragmentCover);
    wepsMetaData.albedo(ifcFile.surfaceAlbedo);
    wepsMetaData.num_layers(ifcFile.layerThickness.length);
    wepsMetaData.surface_thickness(((int) ifcFile.layerThickness[0]));
    wepsMetaData.slope_gradient(ifcFile.surfaceSlope);

    //TODO:  Do we need to measure the layers before setting these??  WEPSSoilInput already filters/sorts layers, etc...
    wepsMetaData.aggStability(ifcFile.aggregateStability[0]);
    wepsMetaData.soilWiltPoint(ifcFile.wiltingPointSWC[0]);
    wepsMetaData.aggGeomDiam(ifcFile.aggregateMeanDiameter[0]);

    String biomass_avg = getOriginalResponse("average_biomass");
    if (!biomass_avg.isEmpty()) {
      wepsMetaData.rotWeightResBiomass(Double.parseDouble(biomass_avg) * 2000);
    } else {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += "Missing biomass_avg result in WEPS output result file.";
    }

    String stir_avg = getOriginalResponse("avg_all_stir");
    if (!stir_avg.isEmpty()) {
      wepsMetaData.rotWeightSoilTillIntensity(Double.parseDouble(stir_avg));
    } else {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += "Missing avg_all_stir result in WEPS output result file.";
    }

    String wind_eros = getOriginalResponse("wind_eros");
    if (!wind_eros.isEmpty()) {
      wepsMetaData.erosionRate(Double.parseDouble(wind_eros));
    } else {
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += "Missing wind_eros result in WEPS output result file.";
    }

    wepsMetaData.errorMessages(badModelMessage);

  }

  private WEPSManagement getManagementFile(byte[] fileData) throws IOException {
    WEPSManagement wepsManagement = new WEPSManagement(new Urls());

    File managementFile = new File("management.man");
    BufferedWriter bufferWriter = Files.newBufferedWriter(managementFile.toPath());
    String data = getFileContents(fileData, ".man");
    bufferWriter.write(data);
    bufferWriter.flush();
    bufferWriter.close();

    try {
      wepsManagement.readManData(managementFile);
    } catch (ParserException | ServiceException ex) {
      Logger.getLogger(WEPSModelArchive.class.getName()).log(Level.SEVERE, null, ex);
      badModelRun = true;
      badModelMessage += "##\n";
      badModelMessage += "Error parsing the WEPS management file:  " + ex.getMessage();
    }

    return wepsManagement;
  }

  private WindGenData getWindDataFile(byte[] fileData) throws IOException {
    WindGenData wind;

    wind = new WindGenData(getFileContents(fileData, ".win"));

    return wind;
  }

  private CligenData getClimateDataFile(byte[] fileData) throws IOException {
    CligenData climate;

    climate = new CligenData(getFileContents(fileData, ".cli"));

    return climate;
  }

  private IFCFile getIFCFile(byte[] fileData) throws IOException {
    IFCFile requestData;

    requestData = new IFCFile(getFileContents(fileData, ".ifc"));

    return requestData;
  }

  private String getStdErrorFile(byte[] fileData) throws IOException {
    String fileString = null;

    try (ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(fileData))) {
      ZipEntry entry;

      while ((entry = zin.getNextEntry()) != null) {
        if (entry.getName().contains("weps.") && entry.getName().contains("stderr.txt")) {
          BufferedReader bReader = new BufferedReader(new InputStreamReader(zin));
          StringBuilder fileContent = new StringBuilder();
          String inputStr;
          while ((inputStr = bReader.readLine()) != null) {
            fileContent.append(inputStr).append(System.lineSeparator());
          }
          fileString = fileContent.toString();
          break;
        }
      }
    }

    return fileString;
  }

  private String getStdOutFile(byte[] fileData) throws IOException {
    String fileString = null;

    try (ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(fileData))) {
      ZipEntry entry;

      while ((entry = zin.getNextEntry()) != null) {
        if (entry.getName().contains("weps.exe") && entry.getName().contains("stdout.txt")) {
          BufferedReader bReader = new BufferedReader(new InputStreamReader(zin));
          StringBuilder fileContent = new StringBuilder();
          String inputStr;
          while ((inputStr = bReader.readLine()) != null) {
            fileContent.append(inputStr).append(System.lineSeparator());
          }
          fileString = fileContent.toString();
          break;
        }
      }
    }

    return fileString;
  }

  public class WEPSMetaData {

    private double latitude;
    private double longitude;
    private String cokey;
    private double windEnergy;
    private double annualPrecip;
    private double rotIrrEffect;
    private double soilWiltPoint;
    private double rotWeightResBiomass;
    private double rotWeightSoilTillIntensity;
    private double rotWeightResAddition;
    private double aggStability;
    private double aggGeomDiam;
    private double crustStability;
    private double surfRockFrag;
    private double erosionRate;
    private String componentName;
    private double[] fractionSand;
    private double[] fractionSilt;
    private double[] fractionClay;
    private double albedo;
    private double slope_gradient;
    private int surface_thickness;
    private int num_layers;
    private String suid;
    private String errorMessages = "";

    public String errorMessages() {
      return errorMessages;
    }

    public void errorMessages(String value) {
      errorMessages = value;
    }

    public String suid() {
      return suid;
    }

    public void suid(String value) {
      suid = value;
    }

    public double windEnergy() {
      return windEnergy;
    }

    public double annualPrecip() {
      return annualPrecip;
    }

    public double rotIrrEffect() {
      return rotIrrEffect;
    }

    public double soilWiltPoint() {
      return soilWiltPoint;
    }

    public double rotWeightResBiomass() {
      return rotWeightResBiomass;
    }

    public double rotWeightSoilTillIntensity() {
      return rotWeightSoilTillIntensity;
    }

    public double rotWeightResAddition() {
      return rotWeightResAddition;
    }

    public double aggStability() {
      return aggStability;
    }

    public double aggGeomDiam() {
      return aggGeomDiam;
    }

    public double crustStability() {
      return crustStability;
    }

    public double surfRockFrag() {
      return surfRockFrag;
    }

    public double erosionRate() {
      return erosionRate;
    }

    public void windEnergy(double value) {
      windEnergy = value;
    }

    public void annualPrecip(double value) {
      annualPrecip = value;
    }

    public void rotIrrEffect(double value) {
      rotIrrEffect = value;
    }

    public void soilWiltPoint(double value) {
      soilWiltPoint = value;
    }

    public void rotWeightResBiomass(double value) {
      rotWeightResBiomass = value;
    }

    public void rotWeightSoilTillIntensity(double value) {
      rotWeightSoilTillIntensity = value;
    }

    public void rotWeightResAddition(double value) {
      rotWeightResAddition = value;
    }

    public void aggStability(double value) {
      aggStability = value;
    }

    public void aggGeomDiam(double value) {
      aggGeomDiam = value;
    }

    public void crustStability(double value) {
      crustStability = value;
    }

    public void surfRockFrag(double value) {
      surfRockFrag = value;
    }

    public void erosionRate(double value) {
      erosionRate = value;
    }

    public String componentName() {
      return componentName;
    }

    public double fractionSand(int index) {
      if ((null != fractionSand) && (index < fractionSand.length)) {
        return fractionSand[index];
      }
      return Double.NaN;
    }

    public double fractionSilt(int index) {
      if ((null != fractionSand) && (index < fractionSilt.length)) {
        return fractionSilt[index];
      }
      return Double.NaN;
    }

    public double fractionClay(int index) {
      if ((null != fractionSand) && (index < fractionClay.length)) {
        return fractionClay[index];
      }
      return Double.NaN;
    }

    public void componentName(String value) {
      componentName = value;
    }

    public void fractionSand(double[] value) {
      fractionSand = value;
    }

    public void fractionSilt(double[] value) {
      fractionSilt = value;
    }

    public void fractionClay(double[] value) {
      fractionClay = value;
    }

    public void albedo(double value) {
      albedo = value;
    }

    public void slope_gradient(double value) {
      slope_gradient = value;
    }

    public void surface_thickness(int value) {
      surface_thickness = value;
    }

    public void num_layers(int value) {
      num_layers = value;
    }

    public double albedo() {
      return albedo;
    }

    public double slope_gradient() {
      return slope_gradient;
    }

    public int surface_thickness() {
      return surface_thickness;
    }

    public int num_layers() {
      return num_layers;
    }

    public double latitude() {
      return latitude;
    }

    public double longitude() {
      return longitude;
    }

    public String cokey() {
      return cokey;
    }

    public void latitude(double value) {
      latitude = value;
    }

    public void longitude(double value) {
      longitude = value;
    }

    public void cokey(String value) {
      cokey = value;
    }

    public JSONArray toJSON() throws JSONException {
      JSONArray ret_val = new JSONArray();
      JSONObject results = new JSONObject();

      results.put("weps_archive_suid", wepsMetaData.suid());
      results.put("annual_precipitation", wepsMetaData.annualPrecip());
      results.put("wind_energy", wepsMetaData.windEnergy());
      results.put("crust_stability", wepsMetaData.crustStability());
      results.put("surface_rock_fragments", wepsMetaData.surfRockFrag());
      results.put("aggregate_stability", wepsMetaData.aggStability());
      results.put("soil_wilting_poiont", wepsMetaData.soilWiltPoint());
      results.put("rotation_weighted_biomass", wepsMetaData.rotWeightResBiomass());
      results.put("annual_precipitation", wepsMetaData.annualPrecip());

      results.put("rotation_weighted_soil_tillage_intensity", wepsMetaData.rotWeightSoilTillIntensity());
      results.put("erosion_rate", wepsMetaData.erosionRate());

      ret_val.put(results);
      return ret_val;
    }
  }
}