WEPPModelArchive.java [tools/MetaModelTools/src/models] Revision: f13b17137cf09777c558d6fb6a492f8e12884fb8  Date: Wed Dec 11 10:22:48 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);
    }

    public WEPPModelArchive(JSONObject metaData) throws JSONException {
	super(metaData);
    }     
    
    public WEPPModelArchive(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 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.";
	    }
	}

        // TODO:  Change to getting the ROT file.
//        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.soilName);

	weppMetaData.num_layers(solFile.numLayers);
	weppMetaData.surface_thickness(((int) solFile.solthk[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 = getOriginalResponse("STIR");
	if (!stir.isEmpty()) {
	    weppMetaData.rotWeightSoilTillIntensity(Double.parseDouble(stir));
	} else {
	    badModelRun = true;
	    badModelMessage += "##\n";
	    badModelMessage += "Missing avg_all_stir result in WEPP output result file.";
	}

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

	String respAvgAnnualPrecip = getOriginalResponse("Precipitation");
	if (!respAvgAnnualPrecip.isEmpty()) {
	    weppMetaData.respAvgAnnualPrecip(Double.parseDouble(respAvgAnnualPrecip));
	} 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.rot");
	BufferedWriter bufferWriter = Files.newBufferedWriter(managementFile.toPath());
	String data = getFileContents(fileData, ".rot");
	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 respAvgAnnualPrecip;        
        private double avgAnnualPrecipEventDuration;
        private double rillErodability;
        private double wAvgSoilSandFraction;
        private double wAvgSoilClayFraction;    
        private double effectiveSurfaceConductivity;
        private int contouring;
        private double lsFactor;
        private double erosionRate;       
	private double rotIrrEffect;
        private double rotWeightCoverCropEffect;
	private double rotWeightResBiomass;
	private double rotWeightSoilTillIntensity;
	private double rotWeightResAddition;
	private String componentName;
	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 respAvgAnnualPrecip(){
            return respAvgAnnualPrecip;
        }

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

	public double rotWeightResBiomass() {
	    return rotWeightResBiomass;
	}

	public double rotWeightSoilTillIntensity() {
	    return rotWeightSoilTillIntensity;
	}

	public double rotWeightResAddition() {
	    return rotWeightResAddition;
	}

	public double erosionRate() {
	    return erosionRate;
	}

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

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

	public void rotIrrEffect(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 String componentName() {
	    return componentName;
	}

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

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

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

	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("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.erosionRate());

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