WindGenData.java [tools/MetaModelTools/src/data/interpretors] Revision: ec5f4cade4553a8341e1cd241b111bfdb77a87a8  Date: Fri Jan 10 10:59:55 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 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;
  private double avgPeriodWindEnergy = 0.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;
    avgPeriodWindEnergy = 0.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:")).trim();
            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 < MAX_YEARS)) {
              double periodWindEnergy = 0.0;

              for (int i = 4; i < 28; i++) {
                double dayValue = Double.parseDouble(tokens[i]);
                dayAverage += dayValue;
                if ((dayValue > MPS_THRESHOLD) && (((month >= 2) && (month <= 4)) || ((month >= 8) && (month <= 10)))) {
                  periodWindEnergy += (0.6 * (dayValue - MPS_THRESHOLD) * Math.pow(dayValue, 2.0) * 0.01157405741);
                }
              }
              if (periodWindEnergy > 0.0) {
                avgPeriodWindEnergy += periodWindEnergy;
              }

              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)) {
      avgPeriodWindEnergy /= yearCount;

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

      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];
    return avgPeriodWindEnergy;
  }

  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;
  }
}