WEPSModelArchive.java [tools/MetaModelTools/src/models] Revision: 0b44430039e6578fe172e57adc541a53cc28d648  Date: Fri Jan 10 10:06:08 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;
        }
    }
}