guiLOADEST_Model.java [src/java/cfa] Revision: 4daefd1ac3a5cce6d2af07d219b133db7ce0b7a4  Date: Thu Sep 26 16:17:42 MDT 2013
package cfa;
import csip.Config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import oms3.util.ProcessComponent;
import utils.BinUtils;

/**
* Last Updated: 26-August-2013
* @author Tyler Wible & Tyler Dell
* @since 27-March-2013
*/
public class guiLOADEST_Model {
    //Inputs
    String mainFolder = "C:/Projects/TylerWible/CodeDirectories/NetBeans/CSIP/data/CFA/LOADEST";
    String organizationName = "USGS";//"Colorado Dept. of Public Health & Environment";//
    String stationID = "06752000";//"06752000";//"06741510";//"EF-01";//"000028";//
    String stationName = "BIG THOMPSON RIVER AT LOVELAND, CO.";
    String wqTest = "00600        Total nitrogen, water, unfiltered, milligrams per liter, mg/L";
    int PTOPT = 1; // estimated values print option (0,1)
    int SEOPT = 2; // standard error option (1-3)
    int LDOPT = 0; // load option (0-3), if load option is 1 or 3 then need the number of user-defined seasons
    int modelNumber = 11;  // (0-11, 99) this picks which model to run if 0 then chooses best model from 1-9 based on error values    
    // if model number is 10, 11 user must specify 
    int PBMON = 5; // Begin month for periodic terms
    int PEMON = 7; // End month for periodic terms
    int ULFLAG = 4; // the desired output units (1-4)
    String beginDate = "";
    String endDate = "";
    String userData = "";//"Date\tFlow\n1994-01-01\t37\n1994-01-02\t39\n1994-01-03\t40\n1994-01-04\t40\n1994-01-05\t40\n1994-01-06\t38\n1994-01-07\t44\n1994-01-08\t37\n1994-01-09\t37\n1994-01-10\t37\n1994-01-11\t35\n1994-01-12\t31\n1994-01-13\t31\n1994-01-14\t27\n1994-01-15\t27\n1994-01-16\t32\n1994-01-17\t25\n1994-01-18\t25\n1994-01-19\t29\n1994-01-20\t32\n1994-01-21\t31\n1994-01-22\t24\n1994-01-23\t29\n1994-01-24\t26\n1994-01-25\t31\n1994-01-26\t30\n1994-01-27\t30\n1994-01-28\t30\n1994-01-29\t24\n1994-01-30\t25\n1994-01-31\t24\n1994-02-01\t28\n1994-02-02\t26\n1994-02-03\t25\n1994-02-04\t26\n1994-02-05\t27\n1994-02-06\t26\n1994-02-07\t25\n1994-02-08\t27\n1994-02-09\t23\n1994-02-10\t22\n1994-02-11\t26\n1994-02-12\t29\n1994-02-13\t26\n1994-02-14\t26\n1994-02-15\t38\n1994-02-16\t40\n1994-02-17\t42\n1994-02-18\t41\n1994-02-19\t44\n1994-02-20\t44\n1994-02-21\t41\n1994-02-22\t34\n1994-02-23\t30\n1994-02-24\t34\n1994-02-25\t34\n1994-02-26\t36\n1994-02-27\t39\n1994-02-28\t54\n1994-03-01\t50\n1994-03-02\t49\n1994-03-03\t51\n1994-03-04\t51\n1994-03-05\t52\n1994-03-06\t54\n1994-03-07\t56\n1994-03-08\t51\n1994-03-09\t43\n1994-03-10\t54\n1994-03-11\t50\n1994-03-12\t46\n1994-03-13\t49\n1994-03-14\t51\n1994-03-15\t50\n1994-03-16\t48\n1994-03-17\t32\n1994-03-18\t56\n1994-03-19\t55\n1994-03-20\t59\n1994-03-21\t55\n1994-03-22\t43\n1994-03-23\t48\n1994-03-24\t46\n1994-03-25\t42\n1994-03-26\t43\n1994-03-27\t35\n1994-03-28\t28\n1994-03-29\t42\n1994-03-30\t38\n1994-03-31\t40\n1994-04-01\t41\n1994-04-02\t41\n1994-04-03\t39\n1994-04-04\t39\n1994-04-05\t41\n1994-04-06\t38\n1994-04-07\t36\n1994-04-08\t35\n1994-04-09\t37\n1994-04-10\t43\n1994-04-11\t41\n1994-04-12\t56\n1994-04-13\t56\n1994-04-14\t52\n1994-04-15\t56\n1994-04-16\t53\n1994-04-17\t56\n1994-04-18\t64\n1994-04-19\t69\n1994-04-20\t76\n1994-04-21\t87\n1994-04-22\t102\n1994-04-23\t130\n1994-04-24\t171\n1994-04-25\t244\n1994-04-26\t223\n1994-04-27\t176\n1994-04-28\t163\n1994-04-29\t158\n1994-04-30\t137\n1994-05-01\t138\n1994-05-02\t138\n1994-05-03\t136\n1994-05-04\t149\n1994-05-05\t149\n1994-05-06\t147\n1994-05-07\t188\n1994-05-08\t291\n1994-05-09\t401\n1994-05-10\t528\n1994-05-11\t476\n1994-05-12\t598\n1994-05-13\t752\n1994-05-14\t874\n1994-05-15\t1030\n1994-05-16\t1050\n1994-05-17\t1310\n1994-05-18\t1180\n1994-05-19\t1130\n1994-05-20\t1270\n1994-05-21\t1100\n1994-05-22\t1110\n1994-05-23\t1200\n1994-05-24\t1090\n1994-05-25\t1090\n1994-05-26\t1180\n1994-05-27\t1160\n1994-05-28\t1270\n1994-05-29\t1350\n1994-05-30\t1390\n1994-05-31\t1490\n1994-06-01\t1590\n1994-06-02\t1380\n1994-06-03\t1220\n1994-06-04\t1210\n1994-06-05\t1250\n1994-06-06\t1270\n1994-06-07\t1190\n1994-06-08\t1010\n1994-06-09\t1040\n1994-06-10\t953\n1994-06-11\t960\n1994-06-12\t916\n1994-06-13\t757\n1994-06-14\t692\n1994-06-15\t702\n1994-06-16\t722\n1994-06-17\t714\n1994-06-18\t700\n1994-06-19\t703\n1994-06-20\t767\n1994-06-21\t819\n1994-06-22\t764\n1994-06-23\t764\n1994-06-24\t686\n1994-06-25\t585\n1994-06-26\t499\n1994-06-27\t420\n1994-06-28\t385\n1994-06-29\t335\n1994-06-30\t278\n1994-07-01\t246\n1994-07-02\t274\n1994-07-03\t291\n1994-07-04\t312\n1994-07-05\t254\n1994-07-06\t175\n1994-07-07\t226\n1994-07-08\t297\n1994-07-09\t233\n1994-07-10\t155\n1994-07-11\t219\n1994-07-12\t254\n1994-07-13\t261\n1994-07-14\t268\n1994-07-15\t229\n1994-07-16\t185\n1994-07-17\t185\n1994-07-18\t267\n1994-07-19\t305\n1994-07-20\t303\n1994-07-21\t300\n1994-07-22\t276\n1994-07-23\t269\n1994-07-24\t327\n1994-07-25\t308\n1994-07-26\t296\n1994-07-27\t268\n1994-07-28\t248\n1994-07-29\t224\n1994-07-30\t215\n1994-07-31\t203\n1994-08-01\t237\n1994-08-02\t302\n1994-08-03\t350\n1994-08-04\t345\n1994-08-05\t346\n1994-08-06\t336\n1994-08-07\t315\n1994-08-08\t293\n1994-08-09\t289\n1994-08-10\t296\n1994-08-11\t319\n1994-08-12\t316\n1994-08-13\t305\n1994-08-14\t306\n1994-08-15\t291\n1994-08-16\t189\n1994-08-17\t160\n1994-08-18\t144\n1994-08-19\t154\n1994-08-20\t251\n1994-08-21\t264\n1994-08-22\t255\n1994-08-23\t245\n1994-08-24\t243\n1994-08-25\t227\n1994-08-26\t207\n1994-08-27\t147\n1994-08-28\t132\n1994-08-29\t122\n1994-08-30\t70\n1994-08-31\t68\n1994-09-01\t57\n1994-09-02\t61\n1994-09-03\t82\n1994-09-04\t102\n1994-09-05\t119\n1994-09-06\t119\n1994-09-07\t123\n1994-09-08\t108\n1994-09-09\t76\n1994-09-10\t48\n1994-09-11\t47\n1994-09-12\t45\n1994-09-13\t43\n1994-09-14\t47\n1994-09-15\t53\n1994-09-16\t46\n1994-09-17\t35\n1994-09-18\t35\n1994-09-19\t34\n1994-09-20\t35\n1994-09-21\t40\n1994-09-22\t72\n1994-09-23\t83\n1994-09-24\t65\n1994-09-25\t46\n1994-09-26\t39\n1994-09-27\t33\n1994-09-28\t26\n1994-09-29\t23\n1994-09-30\t22\n1994-10-01\t20\n1994-10-02\t27\n1994-10-03\t28\n1994-10-04\t33\n1994-10-05\t27\n1994-10-06\t23\n1994-10-07\t31\n1994-10-08\t28\n1994-10-09\t24\n1994-10-10\t21\n1994-10-11\t21\n1994-10-12\t21\n1994-10-13\t21\n1994-10-14\t18\n1994-10-15\t18\n1994-10-16\t18\n1994-10-17\t26\n1994-10-18\t27\n1994-10-19\t19\n1994-10-20\t12\n1994-10-21\t15\n1994-10-22\t13\n1994-10-23\t17\n1994-10-24\t21\n1994-10-25\t17\n1994-10-26\t18\n1994-10-27\t17\n1994-10-28\t18\n1994-10-29\t22\n1994-10-30\t24\n1994-10-31\t28\n1994-11-01\t21\n1994-11-02\t17\n1994-11-03\t22\n1994-11-04\t23\n1994-11-05\t13\n1994-11-06\t25\n1994-11-07\t30\n1994-11-08\t32\n1994-11-09\t25\n1994-11-10\t15\n1994-11-11\t18\n1994-11-12\t22\n1994-11-13\t28\n1994-11-14\t21\n1994-11-15\t6.8\n1994-11-16\t6.0\n1994-11-17\t15\n1994-11-18\t18\n1994-11-19\t23\n1994-11-20\t28\n1994-11-21\t34\n1994-11-22\t36\n1994-11-23\t21\n1994-11-24\t13\n1994-11-25\t23\n1994-11-26\t37\n1994-11-27\t29\n1994-11-28\t10\n1994-11-29\t11\n1994-11-30\t15\n1994-12-01\t28\n1994-12-02\t60\n1994-12-03\t62\n1994-12-04\t36\n1994-12-05\t22\n1994-12-06\t31\n1994-12-07\t23\n1994-12-08\t25\n1994-12-09\t12\n1994-12-10\t15\n1994-12-11\t24\n1994-12-12\t24\n1994-12-13\t31\n1994-12-14\t35\n1994-12-15\t34\n1994-12-16\t37\n1994-12-17\t32\n1994-12-18\t21\n1994-12-19\t21\n1994-12-20\t22\n1994-12-21\t22\n1994-12-22\t22\n1994-12-23\t19\n1994-12-24\t21\n1994-12-25\t22\n1994-12-26\t22\n1994-12-27\t23\n1994-12-28\t23\n1994-12-29\t22\n1994-12-30\t26\n1994-12-31\t22\n1995-01-01\t22"
            //+ "$$Date\t00600\n1994-01-03\tnodata\n1994-01-13\t0.28\n1994-02-16\t0.29\n1994-03-07\t0.25\n1994-04-20\t0.25\n1994-05-03\t0.25\n1994-06-07\t0.25\n1994-06-14\t0.25\n1994-07-06\t0.25\n1994-08-04\t0.25\n1994-09-02\t0.28\n1994-10-11\t0.25\n1994-11-22\t0.33\n1994-12-13\t0.59";
    boolean mergeDatasets = false;//true;// 
    String mergeMethod = "user";//"public";//"max";//"average";//"min";//
    
    
    //Global variable but not an input
    String fileName = "";
    int dailyctr = 0;
    int monthlyctr = 0;
    int yearlyctr = 0;
 
    //Outputs
    String flowLen = "-1";
    String wqLen = "-1";
    String wqUnits = "?";
    String start = "?";
    String end = "?";
    //Outputs
    double dailymax = -1;
    double dailymin = -1;
    double dailyupperQuartile = -1;
    double dailylowerQuartile = -1;
    double dailymedian = -1;
    double dailymean = -1;
    double dailystandardDeviation = -1;
    double monthlymax = -1;
    double monthlymin = -1;
    double monthlyupperQuartile = -1;
    double monthlylowerQuartile = -1;
    double monthlymedian = -1;
    double monthlymean = -1;
    double monthlystandardDeviation = -1;
    double yearlymax = -1;
    double yearlymin = -1;
    double yearlyupperQuartile = -1;
    double yearlylowerQuartile = -1;
    double yearlymedian = -1;
    double yearlymean = -1;
    double yearlystandardDeviation = -1;
    
    
    

    //Gets
//    public File getOutputSummary() {
//        return new File(mainFolder, "echo.out");
//    }
    public File getOutputResult1() {
        return new File(mainFolder, fileName + ".ind");
    }
    public File getOutputResult2() {
        return new File(mainFolder, fileName + ".out");
    }
    public File getOutputResult3() {
        return new File(mainFolder, fileName + ".res");
    }
    public File getDailyTimeseries(){
        return new File(mainFolder, "daily_graph.out");
    }
    public File getDailyBoxplot(){
        return new File(mainFolder, "daily_boxplot.out");
    }
    public File getMonthlyTimeseries(){
        return new File(mainFolder, "monthly_graph.out");
    }
    public File getMonthlyBoxplot(){
        return new File(mainFolder, "monthly_boxplot.out");
    }
    public File getYearlyTimeseries(){
        return new File(mainFolder, "yearly_graph.out");
    }
    public File getYearlyBoxplot(){
        return new File(mainFolder, "yearly_boxplot.out");
    }
    public String getFlowLen() {
        return flowLen;
    }
    public String getWQLen() {
        return wqLen;
    }
    public String getWQUnits() {
        return wqUnits;
    }
    public String getStart(){
        return start;
    }
    public String getEnd(){
        return end;
    }
    public String getDailyMax(){
        return String.valueOf(dailymax);
    }
    public String getDailyMin(){
        return String.valueOf(dailymin);
    }
    public String getDailyUpperQuartile(){
        return String.valueOf(dailyupperQuartile);
    }
    public String getDailyLowerQuartile(){
        return String.valueOf(dailylowerQuartile);
    }
    public String getDailyMedian(){
        return String.valueOf(dailymedian);
    }
    public String getDailyMean(){
        return String.valueOf(dailymean);
    }
    public String getDailyStandardDeviation(){
        return String.valueOf(dailystandardDeviation);
    }
    public String getMonthlyMax(){
        return String.valueOf(monthlymax);
    }
    public String getMonthlyMin(){
        return String.valueOf(monthlymin);
    }
    public String getMonthlyUpperQuartile(){
        return String.valueOf(monthlyupperQuartile);
    }
    public String getMonthlyLowerQuartile(){
        return String.valueOf(monthlylowerQuartile);
    }
    public String getMonthlyMedian(){
        return String.valueOf(monthlymedian);
    }
    public String getMonthlyMean(){
        return String.valueOf(monthlymean);
    }
    public String getMonthlyStandardDeviation(){
        return String.valueOf(monthlystandardDeviation);
    }
    public String getYearlyMax(){
        return String.valueOf(yearlymax);
    }
    public String getYearlyMin(){
        return String.valueOf(yearlymin);
    }
    public String getYearlyUpperQuartile(){
        return String.valueOf(yearlyupperQuartile);
    }
    public String getYearlyLowerQuartile(){
        return String.valueOf(yearlylowerQuartile);
    }
    public String getYearlyMedian(){
        return String.valueOf(yearlymedian);
    }
    public String getYearlyMean(){
        return String.valueOf(yearlymean);
    }
    public String getYearlyStandardDeviation(){
        return String.valueOf(yearlystandardDeviation);
    }
    
    //Sets
    public void setMainFolder(String mainFolder) {
        this.mainFolder = mainFolder;
    }
    public void setOrganizationName(String organizationName) {
        this.organizationName = organizationName;
    }
    public void setStationName(String stationName) {
        this.stationName = stationName;
    }
    public void setStationID(String stationID) {
        this.stationID = stationID;
    }
    public void setWQtest(String wqTest) {
        this.wqTest = wqTest;
    }
    public void setPTOPT(int PTOPT) {
        this.PTOPT = PTOPT;
    }
    public void setSEOPT(int SEOPT) {
        this.SEOPT = SEOPT;
    }
    public void setLDOPT(int LDOPT) {
        this.LDOPT = LDOPT;
    }
    public void setmodelNumber(int modelNumber){
        this.modelNumber = modelNumber;
    }
    public void setPBMON(int PBMON){
        this.PBMON = PBMON;
    }
    public void setPEMON(int PEMON){
        this.PEMON = PEMON;
    }
    public void setULFLAG (int ULFLAG){
        this.ULFLAG = ULFLAG;
    }
    public void setBeginDate(String beginDate) {
        this.beginDate = beginDate;
    }
    public void setEndDate(String endDate) {
        this.endDate = endDate;
    }
    public void setUserData(String userData) {
        this.userData = userData;
    }
    public void setMergeDatasets(boolean mergeDatasets) {
        this.mergeDatasets = mergeDatasets;
    }
    public void setMergeMethod(String mergeMethod) {
        this.mergeMethod = mergeMethod;
    }
    /**
     * Control File Writer:
     * This function will write the control file which is what LOADEST will look for to find the 
     * header, calibration, and estimation files.
     * @throws IOException
     */
    public void writeControl()throws IOException {
        String path = mainFolder + File.separator + "CONTROL.INP";
        FileWriter writer =  new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(writer);

        //Create the control file
        print_line.printf("%s" + "%n", "######################################################################");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "#  LOADEST Control File");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "# line        name of the:");
        print_line.printf("%s" + "%n", "# ----        ------------");
        print_line.printf("%s" + "%n", "#  1          header file");
        print_line.printf("%s" + "%n", "#  2          calibration file");
        print_line.printf("%s" + "%n", "#  3          estimation file");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "######################################################################");
        print_line.printf("%s" + "%n", "HEADER.INP");
        print_line.printf("%s" + "%n", "CALIB.INP");
        print_line.printf("%s" + "%n", "EST.INP");

        print_line.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);
    }
    /**
     * Header File Writer:
     * This function will write the header file that is needed for LOADEST
     * @throws IOException
     */
    public void writeHeader()throws IOException {
        String path = mainFolder + File.separator + "HEADER.INP";
        FileWriter writer =  new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(writer);

        // Introduction for header file
        print_line.printf("%s" + "%n", "######################################################################");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "#  LOADEST Header File");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "# " + stationID + " " + stationName + " " + "Agency: " + organizationName );
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "######################################################################");

        // Inputs 1-4 Title, estimated values print option, standard error option, and load option 
        print_line.printf("%s" + "%n",stationID + " " +stationName + " " + "Agency: " + organizationName);
        print_line.printf("%s" + "%n", PTOPT);
        print_line.printf("%s" + "%n", SEOPT);
        print_line.printf("%s" + "%n", LDOPT);
        print_line.printf("%s" + "%n", "###########################################################");

        // Inputs 5-6 are dependent on what the user defines for the load option 
        // 5 is the number of seasons desired by the user and 6 is the start and end dates for each season
        // do not currenlty allow user to define seasons, only give option for yearly or monthly estimates
        // i.e. LDOPT = 0 or 2, but if want to define seasons modify code below
//        if (LDOPT == 1 || LDOPT == 3){  
//            print_line.printf("%s" + "%n", NSEAS);
//            print_line.printf("%s" + "%n", "#");
//            print_line.printf("%s" + "%n", "#SBEG      SEND");
//            int NSEAS = 1;
//            for (int i=0; i < NSEAS; i++){
//                print_line.printf("%s" + "%n",seasonBegin[i] + "       " + seasonEnd[i] );
//            }
//            print_line.printf("%s" + "%n", "###########################################################");
//        }

        // Input 7 the model number
        print_line.printf("%s" + "%n", modelNumber);
        print_line.printf("%s" + "%n", "###########################################################");
        // Inputs 8-11 are dependent on what the user defines for the model number.
        //If model number is 10 or 11
        if (modelNumber == 10 || modelNumber == 11){
            print_line.printf("%s" + "%n", "#PBMN  PEMN");
            print_line.printf("%s" + "%n", PBMON + "       " + PEMON); // input 8
            print_line.printf("%s" + "%n", "###########################################################");
        }

        // Input 12, number of constituents = 1
        print_line.printf("%s" + "%n", "1");
        print_line.printf("%s" + "%n", "###########################################################");

        // Input 13, is dependent on what the user defines for the number of constituents
        if (fileName.length() > 43){
            fileName = fileName.substring(0,43);
        }
        print_line.printf("%-45s%-5s%-5s" + "%n", fileName, "1", ULFLAG);  // 1 denotes concentration in mg/L 
        print_line.printf("%s" + "%n", "###########################################################");


//        //Listed below is inputs 12 and 13 for nconst > 1
//        // must make sure to redefine cname, ucflag, and ulflag
//        // Input 12, number of constituents
//        print_line.printf("%s" + "%n", nconst);
//        print_line.printf("%s" + "%n", "###########################################################");
//
//        // Input 13, is dependent on what the user defines for the number of constituents
//        for (int j=0; j < nconst; j++){
//            if (cname[j].length() > 43){
//                cname[j] = cname[j].substring(0,43);
//            }
//            print_line.printf("%-45s%-5s%-5s" + "%n", cname[j], ucflag[j], ulflag[j]); 
//
//        }
//        print_line.printf("%s" + "%n", "###########################################################");

        print_line.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);
    }
    /**
     * Calibration File Writer:
     * This function will write the calibration file that is needed for LOADEST
     * @throws IOException
     */
    public void writeCalibration(String[] CDATE, String[] CTIME, String[] CFLOW, String[] CCONC) throws IOException {
        String path = mainFolder + File.separator + "CALIB.INP";
        FileWriter writer =  new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(writer);
                          
        // Introduction for header file
        print_line.printf("%s" + "%n", "##########################################################################################################");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "#  LOADEST Calibration File");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "# " + stationID + " " + stationName + " " + "Agency: " + organizationName );
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "##########################################################################################################");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%-12s%-10s%-10s%-10s" + "%n", "#CDATE","CTIME","CFLOW","CCONC");
        print_line.printf("%s" + "%n", "##########################################################################################################");
        print_line.printf("%s" + "%n", "#");
        
        // Input the data
        for(int i=0; i < CDATE.length; i++){
            // change the date into format needed for loadest
            CDATE[i] = changeDate(CDATE[i]);
            
            print_line.printf("%-12s%-10s%-10s", CDATE[i], CTIME[i], CFLOW[i]);
            // add in concentrations
            //Arbitrarily define the number of water quality parameters equal to 1, this can be modified later if need be
            //for (int j=0; j < nconst; j++){// column counter if multiple concentrations are desired
            //    print_line.printf("%-10s",CCONC[i][j]);
            //}
            print_line.printf("%-10s",CCONC[i]);
            
            // print a new line
            print_line.printf("%n");
        }
        print_line.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);
    }
    /**
     * Estimation File Writer:
     * This function will write the estimation file needed for LOADEST
     * @throws IOException
     */
    public void writeEstimation(String[] EDATE, String[] ETIME, String[] EFLOW) throws IOException {
        // open the file writer and set path
        String path = mainFolder + File.separator + "EST.INP";
        FileWriter writer =  new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(writer);
        
        // Introduction for header file
        print_line.printf("%s" + "%n", "######################################################################");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "#  LOADEST Estimation File");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "# " + stationID + " " + stationName + " " + "Agency: " + organizationName );
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "######################################################################");
        print_line.printf("%s" + "%n", "# Number of oberservations per day");
        print_line.printf("%s" + "%n", "1");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%s" + "%n", "######################################################################");
        print_line.printf("%s" + "%n", "#");
        print_line.printf("%-12s%-10s%-10s" + "%n", "#EDATE", "ETIME", "EFLOW");
        print_line.printf("%s" + "%n", "######################################################################");
        
        // print the needed values
        for (int i=0; i < EDATE.length; i++){
            // change the date into format needed for loadest
            EDATE[i] = changeDate(EDATE[i]);
            
            print_line.printf("%-12s%-10s%-10s", EDATE[i], ETIME[i], EFLOW[i]);
            // print a new line
            print_line.printf("%n");
        }
        
        // Close the file writer 
        print_line.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);
    }
    /**
     * Date Formatter: 
     * This function will change the date into the format that LOADEST requires
     * @param Date - Old Format
     * @return Date - New Format
     */
    public String changeDate(String date) throws IOException {
        String year = date.substring(0,4); 
        String month = date.substring(5,7);
        String day = date.substring(8);
        
        date = year + month + day;
        return date;
    }
    /**
     * Converts the provided water quality values into mg/l if possible, if not 
     * possible then throws and exception to choose a new water quality test
     * @param units  the current units of the water quality test values in CCONC
     * @param CCONC  an array containing the water quality test values
     * @return  CCONC multiplied by a conversion factor to convert it into mg/l 
     * @throws IOException 
     */
    public String[] convertToLOADESTunits(String units, String[] CCONC) throws IOException{
        //Find the unit conversion for current water quality units
        double conversion = 0;
        if(units.equalsIgnoreCase("g/cm3") || units.equalsIgnoreCase("g/mL @ 20C")){
            conversion = 1000;
        }else if(units.equalsIgnoreCase("g/m3")){
            conversion = 1000/Math.pow(100,3);
        }else if(units.equalsIgnoreCase("mg/l") || units.equalsIgnoreCase("mg/l CaCO3") || units.equalsIgnoreCase("mg/l NH4") || 
                    units.equalsIgnoreCase("mg/l NO3") || units.equalsIgnoreCase("mg/l PO4") || units.equalsIgnoreCase("mg/l SiO2") || 
                    units.equalsIgnoreCase("mg/l as H") || units.equalsIgnoreCase("mg/l as N") || units.equalsIgnoreCase("mg/l as Na") || 
                    units.equalsIgnoreCase("mg/l as P") || units.equalsIgnoreCase("mg/l as S") || units.equalsIgnoreCase("mgC3H6O2/L")){
            conversion = 1;
        }else if(units.equalsIgnoreCase("mg/mL @25C")){
            conversion = 1000;
        }else if(units.equalsIgnoreCase("ng/l") || units.equalsIgnoreCase("pg/mL")){
            conversion = 1/Math.pow(10,6);
        }else if(units.equalsIgnoreCase("ng/m3") || units.equalsIgnoreCase("pg/l")){
            conversion = 1/(Math.pow(10,6)*Math.pow(100,3));
        }else if(units.equalsIgnoreCase("pg/m3")){
            conversion = 1/(Math.pow(10,9)*Math.pow(100,3));
        }else if(units.equalsIgnoreCase("ug/L 2,4-D") || units.equalsIgnoreCase("ug/L U3O8") || units.equalsIgnoreCase("ug/L as As") || 
                    units.equalsIgnoreCase("ug/L as Cl") || units.equalsIgnoreCase("ug/L as N") || units.equalsIgnoreCase("ug/L as P") || 
                    units.equalsIgnoreCase("ug/l") || units.equalsIgnoreCase("ugAtrazn/L")){
            conversion = 1/1000;
        }else if(units.equalsIgnoreCase("ug/m3")){
            conversion = 1/(1000*Math.pow(100,3));
        }else{
            String[] errorMessage = {"LOADEST is only able to operate on water quality units of mg/L. The currently selected water quality test is in units of " + units + ", please select a different water quality test that is in mg/L."};
            writeError(errorMessage);
        }
        
        //Apply conversion to mg/L of current water quality data
        for (int i = 0; i < CCONC.length; i++){
            double value = Double.parseDouble(CCONC[i]);
            CCONC[i] = String.valueOf(value*conversion);
        }
        
        return CCONC;
    }
    /**
     * Computes the daily/monthly/yearly max/min/average/total of the daily dataset provided
     * @param dailyData  a string array with column1 = dates (yyyymmdd format), column2 = value
     * @param timeStep  the desired timestep "daily", "monthly", or "yearly"
     * @param method  the desired method "max", "min", "average", or "total"
     * @return a new string[][] with column 1 = dates (yyyymmdd format for "daily" timeStep,
     * yyyy-mm format for "monthly" timeStep, and yyyy format for "yearly" timeStep) and column2 = values
     */
    public String[][] computeFlowMethod(String[][] dailyData, String timeStep, String method) throws IOException{
        String[][] newData = null;
        if(timeStep.equalsIgnoreCase("daily")){
            return dailyData;
        }else if(timeStep.equalsIgnoreCase("monthly")){
            //Compute the method on the unique monthYears
            newData = computeMethod(dailyData, method, 6);

        }else if(timeStep.equalsIgnoreCase("yearly")){
            //Compute the method on the unique monthYears
            newData = computeMethod(dailyData, method, 4);
        }

        return newData;
    }
	/** 
	 * This will open the file and grab the lines of data from it until it reaches the empty line
	 * @return returns the entire text file in textData
	 * @throws IOException
	 */
    public String[] OpenFile(String path) throws IOException {
	
	FileReader fr = new FileReader(path);
	BufferedReader textReader = new BufferedReader(fr);
	
	int numberOfLines = readLines(path);
	String[] textData = new String[numberOfLines];
	
	for (int i = 0; i < numberOfLines; i++){
	textData[i]=textReader.readLine();
	}

	textReader.close();
        fr.close();
	return textData;
	
	}
    /**
     * This will determine how many lines contain total data. Used to in OpenFile
     * @return
     * @throws IOException
     */
	public int readLines(String path) throws IOException{
	
	FileReader file_to_read = new FileReader(path);
	BufferedReader bf = new BufferedReader (file_to_read);
	
	String aLine;
	int numberOfLines = 0;
	
	while ((aLine=bf.readLine()) != null){
	numberOfLines++;
	
	}
	bf.close();
        file_to_read.close();
	
	return numberOfLines;
	}
    /**
     * Computes the method on the dailyData provided substring-ing the dates from 0-dateLimit to 
     * get a unique set of time periods on which to perform the method
     * @param dailyData  a string array with column1 = dates (yyyy-mm-dd format), column2 = value
     * @param method  the desired method "max", "min", "average", or "total"
     * @param dateLimit  an integer for the limit of the date substring (typically 4 or 7 for the 
     * format yyyy-mm-dd resulting in yyyy and yyyy-mm respectively)
     * @return a new string[][] with column 1 = dates (yyyy-mm-dd format for "daily" timeStep,
     * yyyy-mm format for "monthly" timeStep, and yyyy format for "yearly" timeStep) and column2 = values
     */
    public String[][] computeMethod(String[][] dailyData, String method, int dateLimit) throws IOException{
        DoubleMath doubleMath = new DoubleMath();

        //Find the unique set of months/years for which the method is desired for
        ArrayList<String> uniqueTimeStep = new ArrayList<String>();
        String previousMonthYear = dailyData[0][0].substring(0,dateLimit);
        uniqueTimeStep.add(previousMonthYear);
        for(int i=1; i<dailyData.length; i++){
            //Check if current monthYear is the same or different from the previous
            String currentMonthYear = dailyData[i][0].substring(0,dateLimit);
            if(!previousMonthYear.equalsIgnoreCase(currentMonthYear)){
                uniqueTimeStep.add(currentMonthYear);
                previousMonthYear = currentMonthYear;
            }
        }

        //Loop through daily data, pull out each uniqueTimeStep's dataset and perform the method on that dataset
        String[][] newData = new String[uniqueTimeStep.size()][2];
        ArrayList<Double> currentMonthData = new ArrayList<Double>();
        int ctr=0;	
        for(int i=0; i<dailyData.length; i++){
            if(uniqueTimeStep.get(ctr).equals(dailyData[i][0].substring(0, dateLimit))){
                //If current data = current month, add it to the dataset
                currentMonthData.add(Double.parseDouble(dailyData[i][1]));

            }else{
                //If current data != current month, calculate method on the current month's dataset, save the result and reset the dataset
                newData[ctr][0] = uniqueTimeStep.get(ctr);
                if(method.equalsIgnoreCase("max")){
                        newData[ctr][1] = String.valueOf(doubleMath.max(currentMonthData));
                }else if(method.equalsIgnoreCase("average")){
                        newData[ctr][1] = String.valueOf(doubleMath.Average(currentMonthData));
                }else if(method.equalsIgnoreCase("min")){
                        newData[ctr][1] = String.valueOf(doubleMath.min(currentMonthData));
                }else if(method.equalsIgnoreCase("total")){
                        newData[ctr][1] = String.valueOf(doubleMath.sum(currentMonthData));
                }

                //Reset the dataset and add the current data to it as the new month's data
                currentMonthData.clear();
                currentMonthData.add(Double.parseDouble(dailyData[i][1]));
                ctr++;
            }

            //If on the last point calculate the method on the current dataset
            if(i == dailyData.length-1){
                newData[ctr][0] = uniqueTimeStep.get(ctr);
                if(method.equalsIgnoreCase("max")){
                    newData[ctr][1] = String.valueOf(doubleMath.max(currentMonthData));
                }else if(method.equalsIgnoreCase("average")){
                    newData[ctr][1] = String.valueOf(doubleMath.Average(currentMonthData));
                }else if(method.equalsIgnoreCase("min")){
                    newData[ctr][1] = String.valueOf(doubleMath.min(currentMonthData));
                }else if(method.equalsIgnoreCase("total")){
                    newData[ctr][1] = String.valueOf(doubleMath.sum(currentMonthData));
                }
            }
        }

        return newData;
    }
    /**
     * Writes out the error message, if any, for finding the file and then exits the program
     * @param error  string array to be written as each line of an error message
     * @throws IOException
     */
    public void writeError(String[] error) throws IOException{
        //Output data to text file
        String errorContents = error[0];
        for(int i=1; i<error.length; i++){
            errorContents = errorContents + "\n" + error[i];
        }
        throw new IOException("Error encountered. Please see the following message for details: \n" + errorContents);
    }
     /**
     * This will calculate the max for statistics
     * @param sortedData data by given time step
     * @return the max
     */
    public double CalculateMax(String[][] sortedData){
    	DoubleMath doubleMath = new DoubleMath();
    	//get data
        double[] dataList = new double[sortedData.length];
        for(int i=0; i<sortedData.length; i++){
            dataList[i] = Double.parseDouble(sortedData[i][1]);
        }
      //Call Max function
        double max = Math.round(doubleMath.Min_Max(dataList, true)*1000);
        max = max/1000;
        
    	return max;
    }
     /**
     * This will calculate the min for statistics
     * @param sortedData data by given time step
     * @return the min
     */
    public double CalculateMin(String[][] sortedData){
    	DoubleMath doubleMath = new DoubleMath();
    	//get data
        double[] dataList = new double[sortedData.length];
        for(int i=0; i<sortedData.length; i++){
            dataList[i] = Double.parseDouble(sortedData[i][1]);
        }
      //Call Min function
        double min =  Math.round(doubleMath.Min_Max(dataList, false)*1000);
        min = min/1000;
    	return min;
    }
     /**
     * This will calculate the upper quartile for statistics
     * @param sortedData data by given time step
     * @return the upperquartile
     */
    public double CalculateUQ(String[][] sortedData){
    	DoubleMath doubleMath = new DoubleMath();
    	//get data
        double[] dataList = new double[sortedData.length];
        for(int i=0; i<sortedData.length; i++){
            dataList[i] = Double.parseDouble(sortedData[i][1]);
        }
      //Call Upper Quartile function
        double upperQuartile = Math.round(doubleMath.Percentile_function(dataList,0.75)*1000);
        upperQuartile = upperQuartile/1000;
    	return upperQuartile;
    } 
     /**
     * This will calculate the lower quartile for statistics
     * @param sortedData data by given time step
     * @return the lower quartile
     */
    public double CalculateLQ(String[][] sortedData){
    	DoubleMath doubleMath = new DoubleMath();
    	//get data
        double[] dataList = new double[sortedData.length];
        for(int i=0; i<sortedData.length; i++){
            dataList[i] = Double.parseDouble(sortedData[i][1]);
        }
      //Call Lower Quartile function
        double lowerQuartile = Math.round(doubleMath.Percentile_function(dataList,0.25)*1000);
        lowerQuartile = lowerQuartile/1000;
    	return lowerQuartile;
    }
    /**
     * This will calculate the median for statistics
     * @param sortedData data by given time step
     * @return the median
     */
    public double CalculateMedian(String[][] sortedData){
    	DoubleMath doubleMath = new DoubleMath();
    	//get data
        double[] dataList = new double[sortedData.length];
        for(int i=0; i<sortedData.length; i++){
            dataList[i] = Double.parseDouble(sortedData[i][1]);
        }
      //Call Lower Quartile function
      //Call Median function
        double median = Math.round(doubleMath.Median(dataList)*1000);
        median = median/1000;
    	return median;
    }
    /**
     * This will calculate the mean for statistics
     * @param sortedData data by given time step
     * @return the mean
     */
    public double CalculateMean(String[][] sortedData){
    	DoubleMath doubleMath = new DoubleMath();
    	//get data
        double[] dataList = new double[sortedData.length];
        for(int i=0; i<sortedData.length; i++){
            dataList[i] = Double.parseDouble(sortedData[i][1]);
        }
      //Call Mean function
        double mean = Math.round(doubleMath.Average(dataList)*1000);
        mean = mean/1000;
    	return mean;
    }
    /**
     * This will calculate the standard deviation for statistics
     * @param sortedData data by given time step
     * @return the Standard deviation
     */
    public double CalculateSTDEV(String[][] sortedData){
    	DoubleMath doubleMath = new DoubleMath();
    	//get data
        double[] dataList = new double[sortedData.length];
        for(int i=0; i<sortedData.length; i++){
            dataList[i] = Double.parseDouble(sortedData[i][1]);
        }
      //Call standard deviation
        double standardDeviation = Math.round(doubleMath.StandardDeviationSample(dataList)*1000);
        standardDeviation = standardDeviation/1000;
    	return standardDeviation;
    }
     /**
     * This function will prepare the data that is needed for the filewriters
     * @param EDATE
     * @param ETIME
     * @param EFLOW
     * @throws IOException
     */
    public String[] changeDate2(String[][] data, String timeStep){
        String[] date = new String[data.length];
        if (timeStep.equalsIgnoreCase("daily")){
            for (int i = 0; i < data.length; i++){
                double y = Double.parseDouble(data[i][0].substring(0,4));
                double m = Double.parseDouble(data[i][0].substring(4,6));
                double d = Double.parseDouble(data[i][0].substring(6));
                int year = (int) y;
                int month = (int) m;
                int day = (int) d;
                Calendar newDate = Calendar.getInstance();
                newDate.set(year, month - 1, day, 12, 0, 0);
                date[i] = String.valueOf(newDate.getTimeInMillis()/1000); 
            }
        }else if (timeStep.equalsIgnoreCase("monthly")){
            for (int i = 0; i < data.length; i++){
                double y = Double.parseDouble(data[i][0].substring(0,4));
                double m = Double.parseDouble(data[i][0].substring(4,6));
                int year = (int) y;
                int month = (int) m;
                Calendar newDate = Calendar.getInstance();
                newDate.set(year, month - 1, 1, 12, 0, 0);
                date[i] = String.valueOf(newDate.getTimeInMillis()/1000);
            }
        }else{
            for (int i = 0; i < data.length; i++){
                double y = Double.parseDouble(data[i][0].substring(0,4));
                int year = (int) y;
                Calendar newDate = Calendar.getInstance();
                newDate.set(year, 0, 1, 12, 0, 0);
                date[i] = String.valueOf(newDate.getTimeInMillis()/1000);
            }
        }
        return date;
    }
    
    public void writeTimeSeries(String[] date, String[][] data, String timeStep) throws IOException {
        // open the file writer and set path
        String path = mainFolder + File.separator + timeStep + "_graph.out";
        FileWriter writer =  new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(writer);
        
        // Print the needed values
        for (int i = 0; i < date.length; i++){
            print_line.printf("%s" + "%n", date[i] + "\t" + data[i][1]);
        }
      
        // Close the file writer 
        print_line.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);
    }
 
    public void writeBoxplot(double lowerQuartile, double upperQuartile, double[] outliers, double median, String timeStep) throws IOException {
    	// open the file writer and set path
        String path = mainFolder + File.separator + timeStep + "_boxplot.out";
        FileWriter writer =  new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(writer);
    	double IQR = upperQuartile - lowerQuartile;
        double low = lowerQuartile - 1.5 * IQR;
        double high = upperQuartile + 1.5 * IQR;      
        
        // Print the needed values
        if (timeStep.equalsIgnoreCase("daily")){
            for (int i = 0; i < dailyctr; i ++){
                print_line.printf("%s" + "%n", outliers[i]);
            }
            if (low < dailymin){
                low = dailymin;
            }
            }else if(timeStep.equalsIgnoreCase("monthly")){
                for (int i = 0; i < monthlyctr; i ++){
                    print_line.printf("%s" + "%n", outliers[i]);
                }
                if (low < monthlymin){
                    low = monthlymin;
                }
            }else if(timeStep.equalsIgnoreCase("yearly")){
                for (int i = 0; i < yearlyctr; i ++){
                    print_line.printf("%s" + "%n", outliers[i]);
                }
                if (low < yearlymin){
                    low = yearlymin;
                }
            }
        print_line.printf("%s" + "%n", low + "\t" + lowerQuartile + "\t" + median + "\t" + upperQuartile + "\t" + high);
   
        // Close the file writer 
        print_line.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);
    }
    
      
    /**
     * Primary LOADEST function
     * It calls the subfunctions based on user selection/inputs.
     * Calls STORET or USGS database queries and their respective subfunctions
     * @throws IOException 
     * @throws InterruptedException 
     */
    public void run() throws IOException, InterruptedException {
        //Inputs
        //assert args.length > 0;
        //String mainFolder         = args[0];
        //String fileName         = args[1];
        //String organizationName = args[2];
        //String stationID         = args[3];
        //String stationName        = args[4];
        //String wqTest             = args[5];
        //String beginDate        = args[6];
        //String endDate             = args[7];
        //String timeStep            = args[8];
        //String method            = args[9];
    	
    	// initialize graph variables
        String graphUnits = "??";
    	
        
        //If no date input, make it the maximum of available data
        if(beginDate == null || beginDate.equalsIgnoreCase("")){
            beginDate = "1900-01-01";
        }
        if(endDate == null || endDate.equalsIgnoreCase("")){
            // Pull current date for upper limit of data search
            DateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date currentDate = new Date();
            endDate = desiredDateFormat.format(currentDate);
        }
        
        
        //Initialize the DurationCurve class for subfunctions
        DurationCurve durationCurve = new DurationCurve();
        DoubleArray doubleArray = new DoubleArray();

        //Correct Inputs
        int endIndex = wqTest.lastIndexOf(", ");//"00597      Dissolved nitrogen gas, water, unfiltered, milligrams per liter -- mg/l"
        if(endIndex == -1){
                endIndex = wqTest.lastIndexOf("--");
        }
        //Get Units and conversion for current WQ test
        String WQlabel = wqTest.substring(11,endIndex);//cut off the "98335      " part before the test name and the units after the name
        wqTest = wqTest.substring(0,5);//pull just the 5 digit USGS WQ code
        String units = durationCurve.USGSwqUnits(wqTest);

        String[][] sortableData = new String[0][2];
        String[][] WQdata = new String[0][2];

        if(organizationName.equalsIgnoreCase("USGS")){
            //Search for USGS flow data
            System.out.println("Searching USGS");
            USGS_Data usgs_Data = new USGS_Data();
            sortableData = usgs_Data.USGS_read_FDC(stationID, beginDate, endDate);

            //Retrieve WQ data from USGS website
            USGS_Data usgs_data = new USGS_Data();
            WQdata = usgs_data.USGS_read_LDC(stationID);
            //Extract USGS water quality code current wqTest
            WQdata = usgs_data.minimizeUSGSWQdata(WQdata, wqTest, beginDate, endDate);
            
            //If there is minimal flow data try getting WQ-flow data
            if(sortableData.length < 10){
                //Extract USGS water quality code 00061 for dischage in cfs
                String[][] WQFlow1 = usgs_data.minimizeUSGSWQdata(WQdata, "00061", beginDate, endDate);
                //Extract USGS water quality code 30209 for discharge test in m^3/s (cms)
                String[][] WQFlow2 = usgs_data.minimizeUSGSWQdata(WQdata, "30209", beginDate, endDate);

                //Convert the m^3 to ft^3/s
                for(int i=0; i<WQFlow2.length; i++){
                    WQFlow2[i][1] = Double.toString((Double.parseDouble(WQFlow2[i][1])*(3.2808399*3.2808399*3.2808399)));
                }

                //combine the WQ flows (cfs and converted cms) into a single variable to be used with the Flowdata
                String[][] WQDataflows = usgs_data.mergeMinimizedWQdata(WQFlow1, WQFlow2);

                //Combine flow data and WQ flow data into a variable of dates and flow values to be sorted
                sortableData = usgs_data.mergeMinimizedWQdata(sortableData, WQDataflows);
            }

        }else if(organizationName.equalsIgnoreCase("UserData")){
            //Find the user uploaded data file and uses this for a timeseries graph
            User_Data user_Data = new User_Data();
            Object[] returnArray = user_Data.readUserFileLDC(userData, stationID, beginDate, endDate);
            sortableData = (String[][]) returnArray[0];
            WQdata = (String[][]) returnArray[1];

            
        }else{
            //Search for STORET flow data
            System.out.println("Searching STORET");
            STORET_Data storet_Data = new STORET_Data();
            String zipLocation = storet_Data.downloadSTORET(mainFolder, organizationName, stationID, "flow", beginDate, endDate);

            //Unzip results file and extract all flow and WQ results
            sortableData = storet_Data.Unzip_STORETDownloadFiles(zipLocation, "flow", false);
            WQdata = storet_Data.Unzip_STORETDownloadFiles(zipLocation, wqTest, true);
        }
        
        //Find the user uploaded data file and uses this if merged
        String[][] sortableData_user = new String[0][0];
        String[][] WQdata_user = new String[0][0];
        
        if(mergeDatasets){
            User_Data user_Data = new User_Data();
            Object[] returnArray = user_Data.readUserFileLDC(userData, stationID, beginDate, endDate);
            sortableData_user = (String[][]) returnArray[0];
            WQdata_user = (String[][]) returnArray[1];
            if(sortableData_user.length==0){
                String[] errorMessage = {"There is no available uploaded data for station '" + stationID + "' and the specified date range"};
                writeError(errorMessage);
            }
            if(WQdata_user.length==0){
                String[] errorMessage = {"There is no available uploaded data for station '" + stationID + "' and the specified date range"};
                writeError(errorMessage);
            }
        }
        
       
        //Check if any data exists
        if(sortableData.length==0){
            String[] errorMessageArray = {"There is no available flow data for station '" + stationID + "' and the specified date range. Error: ", "USGSLDC0005"};
            writeError(errorMessageArray);
        }
        if(WQdata.length==0){
            String[] errorMessageArray = {"There are no available '" + wqTest + "' water quality tests for station " + stationID +  " by: " + organizationName + " and the specified date range"};
            writeError(errorMessageArray);
        }
        
        //Sort the Data by date to remove duplicate date entries
        String[][] sortedData = durationCurve.removeDuplicateDates(sortableData);
        String[][] sortedData_user = durationCurve.removeDuplicateDates(sortableData_user);
        
        //Merge the two datasets (if user data is empty nothing will be merged)
        String[][] sortedData_combined = doubleArray.mergeData(sortedData, sortedData_user, mergeMethod);
        String[][] WQdata_combined = doubleArray.mergeData(WQdata, WQdata_user, mergeMethod);
        
        //Write control file
        writeControl();
        
        //Write Header File
        String[] cname = WQlabel.split(",");
        fileName = cname[0].trim();
        fileName = fileName.replace(" ", "");//Remove spaces from the name
        writeHeader();
        
        //Get calibration data
        int ctr=0;
        for(int i = 0; i < WQdata_combined.length; i++){
            for(int j = 0; j < sortedData_combined.length; j++){
                if(WQdata_combined[i][0].equalsIgnoreCase(sortedData_combined[j][0])){
                    //count the number of entries with equal dates
                    ctr++;
                }
            }
        }
        
        // write the calibration variables based on the number of concentration values with a associated flow value
        String[] CDATE = new String[ctr];
        String[] CTIME = new String[ctr];
        String[] CFLOW = new String[ctr];
        String[] CCONC = new String[ctr];
        ctr=0; // to keep position of the arrays 
        for(int i=0;i<WQdata_combined.length;i++){
            for(int j=0; j<sortedData_combined.length;j++){
                if(WQdata_combined[i][0].equalsIgnoreCase(sortedData_combined[j][0])){
                    //Keep the data with both a water quality test and a flow value
                    CDATE[ctr] = WQdata_combined[i][0];
                    CTIME[ctr] = "1200";
                    CFLOW[ctr] = sortedData_combined[j][1];
                    CCONC[ctr] = WQdata_combined[i][1]; // need to make sure that CCONC is in mg/L
                    ctr++;
                }
            }
        }
        CCONC = convertToLOADESTunits(units,CCONC);
        writeCalibration(CDATE, CTIME, CFLOW, CCONC);        
        
        
        //Get estimation dates for LOADEST variables
        String[] EDATE = new String[sortedData_combined.length];
        String[] ETIME = new String[sortedData_combined.length];
        String[] EFLOW = new String[sortedData_combined.length];
        for(int i=0; i<sortedData_combined.length; i++){
            EDATE[i] = sortedData_combined[i][0];
            ETIME[i] = "1200";
            EFLOW[i] = sortedData_combined[i][1];
        }
        writeEstimation(EDATE, ETIME, EFLOW);
        
        
        //Set results
        this.flowLen = String.valueOf(EFLOW.length);
        this.wqLen = String.valueOf(ctr);
        this.wqUnits = units;
        this.start = sortedData_combined[0][0];
        this.end = sortedData_combined[sortedData_combined.length - 1][0];
        
        //Call LOADEST exe
        //Expected Input: "CONTROL.INP", "HEADER.INP", "CALIB.INP", and "EST.INP"
        System.out.println("Calling LOADEST.exe");
        String binDir = Config.getString("m.bin.dir", "/tmp/csip/bin");
        File loadest = BinUtils.unpackResource("/bin/win-x86/loadest.exe", new File(binDir));
        ProcessComponent pc = new ProcessComponent();

        if (File.pathSeparatorChar == ':') {
            pc.exe = Config.getString("wine.path", "/usr/bin/wine");
            pc.args = new String[]{loadest.toString()};
        } else {
            pc.exe = loadest.toString();
            pc.args = new String[]{};
        }
        pc.working_dir = mainFolder;
        pc.execute();
        
        //Expected Output: "echo.out" and fileName + ".ind", fileName + ".out", fileName + ".res"
        if (!new File(mainFolder, "echo.out").exists()) {
            throw new FileNotFoundException("echo.out");
        }
        if (!new File(mainFolder, fileName + ".ind").exists()) {
            throw new FileNotFoundException(fileName + ".ind");
        }
        if (!new File(mainFolder, fileName + ".out").exists()) {
            throw new FileNotFoundException(fileName + ".out");
        }
        if (!new File(mainFolder, fileName + ".res").exists()) {
            throw new FileNotFoundException(fileName + ".res");
        }
        System.out.println("Finished LOADEST.exe");
        
        //call file reader for results
        // need to determine the length of data given by LOADEST
        // grab dailyData using file reader
        
        // Run the path 
        String file = mainFolder + File.separator + fileName + ".ind";
        // 
        String[] aryLines = OpenFile(file);
	
	
	// rctr is used to remove 8 lines of header from the file	
	int rctr = aryLines.length - 8;
	String[] results = new String[rctr];
	String[][] dailyData = new String[rctr][2];
	for (int i = 0; i < rctr; i++){
            results[i] = aryLines[(i+8)];
            dailyData[i][0] = results[i].substring(1,9);
            dailyData[i][1] = results[i].substring(28,38);
	}
       
        //convert results to daily,monthly,yearly
        String[][] dailyresultData = computeFlowMethod(dailyData, "daily", "total");
        String[][] monthlyresultData = computeFlowMethod(dailyData, "monthly", "total");
        String[][] yearlyresultData = computeFlowMethod(dailyData, "yearly", "total");
        String[] dailydate = changeDate2(dailyresultData,"daily");
        String[] monthlydate = changeDate2(monthlyresultData, "monthly");
        String[] yearlydate = changeDate2(yearlyresultData, "yearly");
        
        
        //Calculate stats of data
        // Daily stats
        dailymax = CalculateMax(dailyresultData);
        dailymin = CalculateMin(dailyresultData);
        dailyupperQuartile = CalculateUQ(dailyresultData);
        dailylowerQuartile = CalculateLQ(dailyresultData);
        dailymedian = CalculateMedian(dailyresultData);
        dailymean = CalculateMean(dailyresultData);
        dailystandardDeviation = CalculateSTDEV(dailyresultData);
        // Monthly stats
        monthlymax = CalculateMax(monthlyresultData);
        monthlymin = CalculateMin(monthlyresultData);
        monthlyupperQuartile = CalculateUQ(monthlyresultData);
        monthlylowerQuartile = CalculateLQ(monthlyresultData);
        monthlymedian = CalculateMedian(monthlyresultData);
        monthlymean = CalculateMean(monthlyresultData);
        monthlystandardDeviation = CalculateSTDEV(monthlyresultData);
        // Yearly stats
        yearlymax = CalculateMax(yearlyresultData);
        yearlymin = CalculateMin(yearlyresultData);
        yearlyupperQuartile = CalculateUQ(yearlyresultData);
        yearlylowerQuartile = CalculateLQ(yearlyresultData);
        yearlymedian = CalculateMedian(yearlyresultData);
        yearlymean = CalculateMean(yearlyresultData);
        yearlystandardDeviation = CalculateSTDEV(yearlyresultData);        
        System.out.println(yearlymax + " "+dailymax+" "+monthlymax+ " "+yearlymean+" "+monthlymean+" "+dailymean);
        
        
        // Get daily outliers
        // Find IQR and min and max
    	double dailyIQR = dailyupperQuartile - dailylowerQuartile;
    	// Get the loads into a double variable and compare to the limits for outliers
    	for (int i = 0; i < dailyresultData.length; i++){
    		double value = Double.parseDouble(dailyresultData[i][1]);
    		if(value < (dailylowerQuartile - 1.5 * dailyIQR) || value > (dailyupperQuartile + 1.5 * dailyIQR)){
    			dailyctr = dailyctr + 1;
    		}
    	}
    	double[] dailyoutliers = new double[dailyctr];
    	int dailyctr2 = 0;
    	for (int i = 0; i < dailyresultData.length; i++){
    		double value = Double.parseDouble(dailyresultData[i][1]);
    		if(value < (dailylowerQuartile - 1.5 * dailyIQR) || value > (dailyupperQuartile + 1.5 * dailyIQR)){		
    			dailyoutliers[dailyctr2] = value;
    			dailyctr2 = dailyctr2 + 1;
    		}
    	}
    	
    	// Get monthly outliers
        // Find IQR and min and max
    	double monthlyIQR = monthlyupperQuartile - monthlylowerQuartile;
    	// Get the loads into a double variable and compare to the limits for outliers
    	for (int i = 0; i < monthlyresultData.length; i++){
    		double value = Double.parseDouble(monthlyresultData[i][1]);
    		if(value < (monthlylowerQuartile - 1.5 * monthlyIQR) || value > (monthlyupperQuartile + 1.5 * monthlyIQR)){
    			monthlyctr = monthlyctr + 1;
    		}
    	}
    	double[] monthlyoutliers = new double[monthlyctr];
    	int monthlyctr2 = 0;
    	for (int i = 0; i < monthlyresultData.length; i++){
    		double value = Double.parseDouble(monthlyresultData[i][1]);
    		if(value < (monthlylowerQuartile - 1.5 * monthlyIQR) || value > (monthlyupperQuartile + 1.5 * monthlyIQR)){		
    			monthlyoutliers[monthlyctr2] = value;
    			monthlyctr2 = monthlyctr2 + 1;
    		}
    	}
    	
    	// Get yearly outliers
        // Find IQR and min and max
    	double yearlyIQR = yearlyupperQuartile - yearlylowerQuartile;
    	// Get the loads into a double variable and compare to the limits for outliers
    	for (int i = 0; i < yearlyresultData.length; i++){
    		double value = Double.parseDouble(yearlyresultData[i][1]);
    		if(value < (yearlylowerQuartile - 1.5 * yearlyIQR) || value > (yearlyupperQuartile + 1.5 * yearlyIQR)){
    			yearlyctr = yearlyctr + 1;
    		}
    	}
    	double[] yearlyoutliers = new double[yearlyctr];
    	int yearlyctr2 = 0;
    	for (int i = 0; i < yearlyresultData.length; i++){
    		double value = Double.parseDouble(yearlyresultData[i][1]);
    		if(value < (yearlylowerQuartile - 1.5 * yearlyIQR) || value > (yearlyupperQuartile + 1.5 * yearlyIQR)){		
    			yearlyoutliers[yearlyctr2] = value;
    			yearlyctr2 = yearlyctr2 + 1;
    		}
    	}
    	
        
    	//Put graph file writer call here
    	writeTimeSeries(dailydate, dailyresultData, "daily");
    	writeTimeSeries(monthlydate, monthlyresultData, "monthly");
    	writeTimeSeries(yearlydate, yearlyresultData, "yearly");
        
    	writeBoxplot(dailylowerQuartile,dailyupperQuartile, dailyoutliers, dailymedian, "daily");
    	writeBoxplot(monthlylowerQuartile,monthlyupperQuartile, monthlyoutliers, monthlymedian, "monthly");
    	writeBoxplot(yearlylowerQuartile,yearlyupperQuartile, yearlyoutliers, yearlymedian, "yearly");
        

        // Find what units that LOADEST has given the Load in
        if (ULFLAG == 1){
        	graphUnits = "kg";
        }else if (ULFLAG ==2){
        	graphUnits = "mg";
        }else if (ULFLAG == 3){
        	graphUnits = "lb";
        }else{
        	graphUnits = "tons";
        }

        //Create dynamic summary paragraph
        this.start = sortedData[0][0];
        this.end = sortedData[sortedData.length - 1][0];
        this.wqLen = String.valueOf(sortedData.length);
        this.wqUnits = graphUnits;
    }
    public static void main(String[] args) throws IOException, InterruptedException, Exception {
        guiLOADEST_Model loadest_Model = new guiLOADEST_Model();
        
        //Run model
        loadest_Model.run();
    }
}