WindGenData.java [tools/MetaModelTools/src/data/interpretors] Revision: cc41e45f67fa3e290531b41dca43638a993daf2e  Date: Wed Oct 30 15:09:04 MDT 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 data.interpretors;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

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

    public static final int MPS_THRESHOLD = 8;
    public static final int MAX_YEARS = 100;
    public static final int MAX_YEAR_AVERAGE_INDEX = MAX_YEARS;

    private double[][][] dailyData = new double[MAX_YEARS][12][31];  //Daily Averages
    private double[][] monthlyAverage = new double[MAX_YEARS + 1][12];
    private double[] yearlyAverage = new double[MAX_YEARS + 1];

    private double[][][] dailyEnergyData = new double[MAX_YEARS + 1][12][31];  //Daily Averages
    private double[][] monthlyEnergyData = new double[MAX_YEARS + 1][12];
    private double[] yearlyEnergyData = new double[MAX_YEARS + 1];

    public static int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    private String messages = "";
    private boolean badWindData = false;
    private String stationName = "";
    private String period = "";
    private int elevation = 0;
    private Period windPeriod;
    private int yearCount = 0;

    public WindGenData(String windFileData) {

        for (int year = 0; year < MAX_YEARS; year++) {
            for (int month = 0; month < 12; month++) {
                for (int day = 0; day < 31; day++) {
                    dailyData[year][month][day] = -1.0;
                }
            }
        }
        
        try {
            readWindData(windFileData);
        } catch (IOException ex) {
            Logger.getLogger(WindGenData.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void readWindData(String windFileData) throws IOException {
        BufferedReader windData;
        windData = new BufferedReader(new StringReader(windFileData));

        String windLine;
        int count = 0;
        int febCount = 0;
        boolean foundData = false;
        int lastYear = -1;
        double yearTotal = 0;
        double yearEnergyTotal = 0;
        double monthEnergyTotal = 0;
        int numDays = 0;
        int numMonthDays = 0;
        int lastMonth = -1;
        double monthTotal = 0;

        while ((windLine = windData.readLine()) != null) {
            count++;
            if (!foundData && windLine.startsWith("#")) {
                if (windLine.contains("station:")) {
                    stationName = windLine.substring(windLine.indexOf("station: ") + 9);
                } else {
                    if (windLine.contains("period: ")) {
                        period = windLine.substring(windLine.indexOf("period: ") + 8, windLine.indexOf("el:") - (windLine.indexOf("period: ") + 8));
                        String elString = windLine.substring(windLine.indexOf("el:") + 3);
                        elevation = Integer.parseInt(elString.replace("m", " ").trim());
                    } else {
                        if (windLine.contains("day mo year dir   hr1")) {
                            if ((windLine = windData.readLine()) != null) {
                                if (windLine.contains("deg   m/s  m/s")) {
                                    if ((windLine = windData.readLine()) != null) {
                                        if (windLine.contains("# ----------------")) {
                                            foundData = true;
                                        } else {
                                            messages += "\n#\nMissing comment underline line after deg m/s m/s... line";
                                            badWindData = true;
                                            break;
                                        }
                                    } else {
                                        messages += "\n#\nMissing next line after deg m/s... line";
                                        badWindData = true;
                                        break;
                                    }
                                }
                            } else {
                                messages += "\n#\nMissing next line after day mo year... line";
                                badWindData = true;
                                break;
                            }
                        }
                    }
                }
                continue;
            } else {
                if (foundData) {
                    String[] tokens = windLine.trim().split("\\s+");
                    if (tokens.length >= 28) {
                        int day, month, year;
                        double dayAverage = 0;

                        day = Integer.parseInt(tokens[0]) - 1;
                        month = Integer.parseInt(tokens[1]) - 1;
                        year = Integer.parseInt(tokens[2]) - 1;

                        if (month != lastMonth) {
                            if (lastMonth != -1) {
                                monthlyAverage[lastYear][lastMonth] = monthTotal / numMonthDays;
                                monthlyAverage[MAX_YEARS][lastMonth] += monthlyAverage[lastYear][lastMonth];
                                monthlyEnergyData[lastYear][lastMonth] = monthEnergyTotal;
                            }
                            monthTotal = 0;
                            numMonthDays = 0;
                            lastMonth = month;
                            monthEnergyTotal = 0;
                        }

                        if (year != lastYear) {
                            if (lastYear != -1) {
                                yearlyAverage[lastYear] = yearTotal / numDays;
                                yearlyAverage[MAX_YEARS] += yearlyAverage[lastYear];
                                yearlyEnergyData[lastYear] = yearEnergyTotal;
                            }
                            yearCount++;
                            yearTotal = 0;
                            numDays = 0;
                            lastYear = year;
                            yearEnergyTotal = 0;
                        }

                        if ((year <= period().getYears()) && (year < MAX_YEARS)) {
                            for (int i = 4; i < 28; i++) {
                                dayAverage += Double.parseDouble(tokens[i]);
                            }

                            dayAverage /= 24.0;

                            dailyData[year][month][day] = dayAverage;
                            //dailyData[MAX_YEAR_AVERAGE_INDEX][month][day] += dayAverage;

                            if (dayAverage > MPS_THRESHOLD) {
                                dailyEnergyData[year][month][day] = 0.6 * Math.pow(dayAverage - MPS_THRESHOLD, 2);
                            } else {
                                dailyEnergyData[year][month][day] = 0;
                            }

                            if ((month == 2) && (day == 29)) {
                                febCount++;
                            }

                            dailyEnergyData[MAX_YEARS][month][day] += dailyEnergyData[year][month][day];
                            
                            yearEnergyTotal += dailyEnergyData[year][month][day];
                            monthEnergyTotal += dailyEnergyData[year][month][day];
                            monthTotal += dayAverage;
                            numMonthDays++;
                            yearTotal += dayAverage;
                            numDays++;
                        } else {
                            messages += "\n#\nWind file was longer than 100 years.  Data clipped to 100 years.";
                            break;
                        }
                    } else {
                        messages += "\n#\nMissing hourly wind data on line " + count + "of the windgen data file.  Expected 28 fields and found only " + tokens.length + " fields instead.";
                        badWindData = true;
                        break;
                    }
                } else {
                    // Blank line??  Regardless ignore it for now...maybe generate an error if we discover that this cannot/shoud not happen in a .win file.
                }
            }
        }

        if (foundData && (yearCount > 0)) {

            monthlyAverage[lastYear][lastMonth] = monthTotal / numMonthDays;
            yearlyAverage[lastYear] = yearTotal / numDays;
            monthlyEnergyData[lastYear][lastMonth] = monthEnergyTotal;
            yearlyEnergyData[lastYear] = yearEnergyTotal;
            yearCount++;

            yearlyAverage[MAX_YEARS] /= yearCount;
            yearlyEnergyData[MAX_YEARS] /= yearCount;

            for (int i = 0; i < 12; i++) {
                monthlyAverage[MAX_YEARS][i] /= yearCount;
                monthlyEnergyData[MAX_YEARS][i] /= yearCount;
            }

        }
    }

    public int yearsSimulated() {
        return period().getYears();
    }

    public int yearsInFile() {
        return yearCount;
    }

    public int elevation() {
        return elevation;
    }

    public double[][] monthlyAverages() {
        return monthlyAverage;
    }

    public double monthlyAverage(int year, int month) {
        if ((year < yearCount) && ((month >= 0) && (month < 12))) {
            return monthlyAverage[year][month];
        }

        return Double.NaN;
    }

    public String stationName() {
        return stationName;
    }

    public double yearlyAverage(int yearIndex) {
        if ((yearIndex < yearCount) && (yearIndex >= 0)) {
            return yearlyAverage[yearIndex];
        }

        return Double.NaN;
    }

    public double[] monthlyAverage(int yearIndex) {
        if ((yearIndex < yearCount) && (yearIndex >= 0)) {
            return monthlyAverage[yearIndex];
        }

        return null;
    }

    public double windEnergy(int month, int day, int period) {
        double energyVal = 0;

        if (period > 365) {
            energyVal = Double.NaN;
        } else {
            int currentMonth = month;
            int startDay = day;
            int dayCount = 0;

            while (dayCount < period) {
                for (int tDay = startDay; tDay < monthDays[currentMonth]; tDay++) {
                    energyVal += (dailyEnergyData[MAX_YEARS][currentMonth][tDay] / yearCount);
                    dayCount++;
                }
                startDay = 0;
                currentMonth++;
                if ( currentMonth == 12){
                    currentMonth = 0;
                }
            }
        }

        return energyVal;
    }

    public double simulationAverage() {
        return yearlyAverage[MAX_YEARS];
    }

    public double[] simulationMonthlyAverage() {
        return monthlyAverage[MAX_YEARS];
    }

    public Period period() {
        if (null == windPeriod) {
            String periods[] = period.trim().split("-");
            if (periods.length == 2) {
                DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMdd");
                LocalDate startDate = LocalDate.parse(periods[0], df);
                LocalDate endDate = LocalDate.parse(periods[1], df);
                windPeriod = Period.between(startDate, endDate);

            } else {
                this.badWindData = true;
                this.messages = "\n#\nPeriod string found in file is not properly formatted.";
            }
        }

        return windPeriod;
    }

    public boolean badWindData() {
        return badWindData;
    }

    public String windDataMessages() {
        return messages;
    }
}