CDPHE_lowFlowStats.java [src/java/cfa] Revision: 46c2f714b6c8c6c2e99c47bc1a60914fc136ac78  Date: Thu Dec 18 10:20:44 MST 2014
package cfa;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

/**
* Last Updated: 16-December-2014
* @author Tyler Wible
* @since 16-December-2014
*/
public class CDPHE_lowFlowStats {
     //Gets
    public String getFlowStatistics_summary() {
        return "cdphe_lowflow.csv";
    }
    /**
     * Calculate the CDPHE 'extreme value' design flow (see DFLOW user manual) 
     * based on an 'm'-day average low flow of each year fitted to a Log-Pearson
     * Type III distribution and interpolated for a return period of 'R' years
     * @param mainFolder  the file path where the summary will be saved
     * @param stationID  the station ID for the current station, used to label the output
     * @param stationName  the station name for the current station, used to label the  output
     * @param flowData  flow data, column1 = dates (format yyyy-mm-dd), column2 = flow values
     * @param m  the number of days to average for annual low flow analysis (m-day average)
     * @param R  the desired return period of the m-day low flow (in years)
     * @param waterYearBegin  the month and day (MM-dd) of the start of the water year for this analysis
     * @return
     * @throws IOException
     * @throws ParseException 
     */
    public double CDPHE_ExtremeValue(String mainFolder,
                                     String stationID,
                                     String stationName,
                                     String[][] flowData,
                                     int m,
                                     int R,
                                     String waterYearBegin) throws IOException, ParseException{
        DoubleArray doubleArray =  new DoubleArray();
        DoubleMath doubleMath = new DoubleMath();
        
        //Get today's date for output purposes
        Date currentDate = new Date();
        SimpleDateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String today = desiredDateFormat.format(currentDate);
        stationName = stationName.replace(",", "");
        
        //Initialize the summary result table
        String[][] statsSummaryTable = new String[6][2];
        statsSummaryTable[0][0] = "CDPHE DFlow Statistics for " + stationID + "; " + stationName + "; created on " + today;
        statsSummaryTable[1][0] = "Analysis Period (water year beginning " + waterYearBegin +")";
        statsSummaryTable[2][0] = "Extreme Value-based Design Low Flow, (" + m + "-day minimum) [cfs]";
        statsSummaryTable[3][0] = "Annual Low Flow (" + m + "-day minimum) [cfs]";
        statsSummaryTable[4][0] = "Low Flow Statistics based on 'DFLOW' User's Manual:";
        statsSummaryTable[5][0] = "Rossman, L.A. 'DFLOW User's Manual' U.S. Environmental Protection Agency Cincinnati, Ohio 45265.";
        
        statsSummaryTable[0][1] = "";
        statsSummaryTable[1][1] = "";
        statsSummaryTable[2][1] = "to be added later";
        statsSummaryTable[3][1] = "--";
        statsSummaryTable[4][1] = "";
        statsSummaryTable[5][1] = "";
        
        
        int currentYear = Integer.parseInt(flowData[0][0].substring(0,4));
        int finalYear = Integer.parseInt(flowData[flowData.length - 1][0].substring(0,4));
//        //Start by pulling the first complete water year (determine if this is necessary
//        if(flowData[0][0].compareToIgnoreCase(String.valueOf(currentYear) + "-04-01") > 0){
//            //If the start date of data is after the start of the water year, reset the 
//            //begin date to next year so that only complete water years are analyzed
//            currentYear++;
//        }
        int nextYear = currentYear + 1;
        String beginDate = String.valueOf(currentYear) + "-" + waterYearBegin;//String beginDate = String.valueOf(currentYear) + "-04-01";
        String previousDay = doubleArray.getDay(beginDate, -1);
        String endDate = String.valueOf(nextYear) + previousDay.substring(4);//String endDate = String.valueOf(nextYear) + "-03-31";
        
        //Calculate Flow statistics for each water-year in time period
        ArrayList<Double> mDay_annualNonZero = new ArrayList<Double>();
        int mDay_annual = 0;
        boolean moreYears = finalYear > nextYear;
        while(moreYears){
            //Get current water year's data and calculate it's statistics
            String[][] partialData = doubleArray.getPeriodData(flowData, beginDate, endDate);
            
            //Calculate m-day statistics
            Object[] resultArray = doubleArray.getMdayData(partialData, m, "arithmetic");
            //ArrayList<String> average_Mday_date = (ArrayList<String>) resultArray[0];
            ArrayList<Double> average_Mday = (ArrayList<Double>) resultArray[1];
            
            //Calculate minimum flow and save it
            double mDay_min = doubleMath.min(average_Mday);
            if(mDay_min > 0) mDay_annualNonZero.add(mDay_min);
            mDay_annual++;
            
            //Add this water year's statistics to the existing results
            String waterYearHeader = "Water Year " + String.valueOf(currentYear);
            String[] additionalSummary = {"", waterYearHeader, "--", String.valueOf(mDay_min), "", ""};
            statsSummaryTable = doubleArray.appendcolumn_Matrix(statsSummaryTable, additionalSummary);
            
            //Check if this is the last year
            if(finalYear >= nextYear){
                currentYear++;
                nextYear++;
                beginDate = String.valueOf(currentYear) + "-" + waterYearBegin;//beginDate = String.valueOf(currentYear) + "-04-01";
                previousDay = doubleArray.getDay(beginDate, -1);
                endDate = String.valueOf(nextYear) + previousDay.substring(4);//endDate = String.valueOf(nextYear) + "-03-31";
            }else{
                moreYears = false;
            }
        }
        
        //Convert low flows to natural-log low-flows
        ArrayList<Double> lnMday_annualNonZero = new ArrayList<Double>();
        for(int i=0; i<mDay_annualNonZero.size(); i++){
            lnMday_annualNonZero.add( Math.log(mDay_annualNonZero.get(i)) );
        }
        
        //Determine properties of natural-log low-flows
        double U = doubleMath.meanArithmetic(lnMday_annualNonZero);//Mean
        double S = doubleMath.StandardDeviationSample(lnMday_annualNonZero);//Standard Deviation
        double G = doubleMath.SkewnessSample(lnMday_annualNonZero);//Skewness
        
        //Determine fraction of m-day low flows that are zero
        double f0 = (mDay_annual - mDay_annualNonZero.size()) / mDay_annualNonZero.size();
        
        //Determine cumulative probability of corresponding user-supplied return period
        double P = (1./R - f0) / (1 - f0);
        
        //Determine standard normal deviate from cumulative probability (Joiner and Rosenblatt, 1971)
        double Z = 4.91 * (Math.pow(P, 0.14) - Math.pow(1-P, 0.14));
        
        //Compute gamma deviate, K, using Wilson-Hilferty transformation (Loucks et al., 1981)
        double K = (2./G) * (Math.pow(1 + (G*Z/6) - (G*G/36), 3) - 1);
        
        //Compute design flow
        double designFlow = Math.exp(U + K*S);
        
        //Call file writer for outputs fo flow statistics
        statsSummaryTable[2][0] = String.valueOf(doubleMath.round(designFlow, 3));
        writeStatsSummaryFile(mainFolder, statsSummaryTable);
        
        return designFlow;
    }
    /**
     * Calculate the CDPHE 'human health' design flow (see DFLOW user manual) 
     * which is the harmonic mean of the flows
     * @param mainFolder  the file path where the summary will be saved
     * @param stationID  the station ID for the current station, used to label the output
     * @param stationName  the station name for the current station, used to label the  output
     * @param flowData  flow data, column1 = dates (format yyyy-mm-dd), column2 = flow values
     * @return
     * @throws IOException
     * @throws ParseException 
     */
    public double CDPHE_HumanHealth(String mainFolder,
                                    String stationID,
                                    String stationName,
                                    String[][] flowData) throws IOException, ParseException{
        DoubleMath doubleMath = new DoubleMath();
        
        //Get today's date for output purposes
        Date currentDate = new Date();
        SimpleDateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String today = desiredDateFormat.format(currentDate);
        stationName = stationName.replace(",", "");
        String beginDate = flowData[0][0];
        String endDate = flowData[flowData.length - 1][0];
        
        //Calculate properties of harmonic mean
        double[] flowOnlyData = new double[flowData.length];
        for(int i=0; i<flowData.length; i++){
            flowOnlyData[i] = Double.parseDouble(flowData[i][1]);
        }
        double designFlow = doubleMath.meanHarmonic(flowOnlyData);
        
        //Initialize the summary result table
        String[][] statsSummaryTable = new String[5][2];
        statsSummaryTable[0][0] = "CDPHE DFlow Statistics for " + stationID + "; " + stationName + "; created on " + today;
        statsSummaryTable[1][0] = "Analysis Period";
        statsSummaryTable[2][0] = "Human Health-based Design Low Flow, harmonic mean [cfs]";
        statsSummaryTable[3][0] = "Low Flow Statistics based on 'DFLOW' User's Manual:";
        statsSummaryTable[4][0] = "Rossman, L.A. 'DFLOW User's Manual' U.S. Environmental Protection Agency Cincinnati, Ohio 45265.";
        
        statsSummaryTable[0][1] = "";
        statsSummaryTable[1][1] = beginDate + " to " + endDate;
        statsSummaryTable[2][1] = String.valueOf(doubleMath.round(designFlow, 3));
        statsSummaryTable[3][1] = "";
        statsSummaryTable[4][1] = "";
        
        
        //Call file writer for outputs fo flow statistics
        writeStatsSummaryFile(mainFolder, statsSummaryTable);
        
        return designFlow;
    }
    /**
     * Writes the provided array to a CSV result file
     * @param mainFolder  the folder location for the result file
     * @param statsSummary  the desired contents of the file
     * @throws IOException 
     */
    private void writeStatsSummaryFile(String mainFolder, String[][] statsSummary) throws IOException{
        //Open a file writer for the summary of the flow statistcs per year
        String path = mainFolder + File.separator + getFlowStatistics_summary();
        FileWriter newFile =  new FileWriter(path, false);
        PrintWriter writer = new PrintWriter(newFile);
        
        for(int i=0; i<statsSummary.length; i++){
            String currentLine = statsSummary[i][0];
            for(int j=1; j<statsSummary[i].length; j++){
                currentLine = currentLine + "," + statsSummary[i][j];
            }
            writer.printf("%s" + "\r\n", currentLine);
        }
        
        //Close file writer
        newFile.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);
    }
}