WEPPModelArchive.java [tools/MetaModelTools/src/models] Revision: 0e358549a9f8afb605ba3da9f0f40b311f9e7d16  Date: Fri Dec 06 15:09:07 MST 2019
/*
 * 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 data.interpretors.CligenData;
import data.interpretors.SOLFile;
import data.interpretors.WindGenData;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import nodes.WEPSManagement;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import parsers.ParserException;
import utils.Urls;

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


    private SOLFile solFile;
    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 WEPPMetaData weppMetaData;

    public WEPPModelArchive(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 WEPPModelArchive(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);
    }

    @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 WEPP 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 {
	    solFile = getSolFile(fileData);
	} catch (IOException ex) {
	    badModelRun = true;
	    badModelMessage += "##\n";
	    badModelMessage += ex.getMessage();
	}

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

    public boolean questionableResults() {
	return possibleBadModelRun;
    }

    public boolean badModelRun() {
	return badModelRun;
    }

    public String badModelMessage() {
	return badModelMessage;
    }

    public SOLFile solFile() {
	return solFile;
    }

    public WindGenData windData() {
	return windData;
    }

    public CligenData cligenData() {
	return cligenData;
    }

    public String stdErrorFile() {
	return stdErrorFile;
    }

    public String stdOutFile() {
	return stdOutFile;
    }

    public WEPPMetaData getWEPPMetaData() {
	return weppMetaData;
    }

    public void calcWEPPMetaData() {
	if (null == weppMetaData) {
	    weppMetaData = new WEPPMetaData();
	}
//
//	weppMetaData.suid(this.suid);
//	weppMetaData.cokey(getOriginalRequest("soil"));
//	weppMetaData.longitude(Double.parseDouble(getOriginalRequest("longitude")));
//	weppMetaData.latitude(Double.parseDouble(getOriginalRequest("latitude")));	
//	weppMetaData.annualPrecip(cligenData.annualAvgPrecip());
//	weppMetaData.windEnergy(windData.simulationAverage());
//	weppMetaData.componentName(solFile.componentName);
//	weppMetaData.fractionSand(solFile.fractionSand);
//	weppMetaData.fractionSilt(solFile.fractionSilt);
//	weppMetaData.fractionClay(solFile.fractionClay);
//	weppMetaData.crustStability(solFile.crustStability);
//	weppMetaData.surfRockFrag(solFile.surfaceFragmentCover);
//	weppMetaData.albedo(solFile.surfaceAlbedo);
//	weppMetaData.num_layers(solFile.layerThickness.length);
//	weppMetaData.surface_thickness(((int) solFile.layerThickness[0]));
//	weppMetaData.slope_gradient(solFile.surfaceSlope);
//
//	//TODO:  Do we need to measure the layers before setting these??  WEPPSoilInput already filters/sorts layers, etc...
//	weppMetaData.aggStability(solFile.aggregateStability[0]);
//	weppMetaData.soilWiltPoint(solFile.wiltingPointSWC[0]);

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

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

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

	weppMetaData.errorMessages(badModelMessage);

    }

    private WEPSManagement getManagementFile(byte[] fileData) throws IOException {
	WEPSManagement weppManagement = 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 {
	    weppManagement.readManData(managementFile);
	} catch (ParserException | ServiceException ex) {
	    Logger.getLogger(WEPPModelArchive.class.getName()).log(Level.SEVERE, null, ex);
	    badModelRun = true;
	    badModelMessage += "##\n";
	    badModelMessage += "Error parsing the WEPP management file:  " + ex.getMessage();
	}

	return weppManagement;
    }

    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 SOLFile getSolFile(byte[] fileData) throws IOException {
	SOLFile requestData = null;

	//requestData = new SOLFile(getFileContents(fileData, ".sol"));

	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("wepp.") && 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("wepp.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 WEPPMetaData {

	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 crustStability;
	private double surfRockFrag;
	private double errosionRate;
	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 rotIrrEffect;
	}

	public double rotWeightResBiomass() {
	    return rotWeightResBiomass;
	}

	public double rotWeightSoilTillIntensity() {
	    return rotWeightSoilTillIntensity;
	}

	public double rotWeightResAddition() {
	    return rotWeightResAddition;
	}

	public double aggStability() {
	    return aggStability;
	}

	public double crustStability() {
	    return crustStability;
	}

	public double surfRockFrag() {
	    return surfRockFrag;
	}

	public double errosionRate() {
	    return errosionRate;
	}

	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) {
	    rotIrrEffect = 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 crustStability(double value) {
	    crustStability = value;
	}

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

	public void errosionRate(double value) {
	    errosionRate = 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("wepp_archive_suid", weppMetaData.suid());
	    results.put("annual_precipitation", weppMetaData.annualPrecip());
	    results.put("wind_energy", weppMetaData.windEnergy());
	    results.put("crust_stability", weppMetaData.crustStability());
	    results.put("surface_rock_fragments", weppMetaData.surfRockFrag());
	    results.put("aggregate_stability", weppMetaData.aggStability());
	    results.put("soil_wilting_poiont", weppMetaData.soilWiltPoint());
	    results.put("rotation_weighted_biomass", weppMetaData.rotWeightResBiomass());
	    results.put("annual_precipitation", weppMetaData.annualPrecip());

	    results.put("rotation_weighted_soil_tillage_intensity", weppMetaData.rotWeightSoilTillIntensity());
	    results.put("erosion_rate", weppMetaData.errosionRate());

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