CligenData.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.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author <a href="mailto:shaun.case@colostate.edu">Shaun Case</a>
*/
public class CligenData {
public static final int MAX_YEARS = 100;
public static final int MONTH_AVERAGE_INDEX = 31;
public static final int MAX_YEAR_AVERAGE_INDEX = MAX_YEARS;
public static final int PRCP_INDEX = 0;
public static final int DUR_INDEX = 1;
public static final int TP_INDEX = 2;
public static final int IP_INDEX = 3;
public static final int TMAX_INDEX = 4;
public static final int TMIN_INDEX = 5;
public static final int RAD_INDEX = 6;
public static final int W_VL_INDEX = 7;
public static final int W_DIR_INDEX = 8;
public static final int TDEW_INDEX = 9;
public static final int DATA_TYPES_LENGTH = 10;
public static final int OBS_MONTHLY_TMAX = 0;
public static final int OBS_MONTHLY_TMIN = 1;
public static final int OBS_MONTHLY_RAD = 2;
public static final int OBS_MONTHLY_PRCP = 3;
public static final int OBS_MONTHLY_LENGTH = 4;
private static final int DAILY_VALUE_TOKENS = 13;
// All monthly data for observeredYears years. (Max out, i.e. cap output, at MAX_YEARS yrs)
private double[][][][] dailyData = new double[MAX_YEARS + 1][12][MONTH_AVERAGE_INDEX + 1][DATA_TYPES_LENGTH]; //Daily Averages
private double[][] yearlyData = new double[MAX_YEARS][DATA_TYPES_LENGTH]; //Yearly summaries
// Montly averages, over entire observed years period, for prcp, dur, tp, ip, tmax, tmin, rad, w-vl, w-dir, tdew.
private double[][] monthlyAverage = new double[12][DATA_TYPES_LENGTH];
public static final int[] monthDays = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
private String messages = "";
private boolean badClimateData = false;
private String stationName = "";
// Entire observedYears years averaged by month
private double[][] observedMmonthlyAverage = new double[12][OBS_MONTHLY_LENGTH];
// How many years total in the file
private int observedYears = 0;
private int yearCount = 0;
private int elevation = 0;
private String climateVersion;
private int yearsSimulated;
private int beginningYear;
private double annualAvgPrecip = Double.NaN;
public CligenData(String cliFileData) {
try {
readClimateData(cliFileData);
} catch (IOException ex) {
Logger.getLogger(WindGenData.class.getName()).log(Level.SEVERE, null, ex);
}
}
public boolean badClimateData() {
return badClimateData;
}
public String cligenDataMessages() {
return messages;
}
/**
* Calculates if not yet calculated, the annual average precipitation based
* on "observed" monthly averages found at the top of the climate file. This
* does not represent the actual yearly or yearsSimulated averages, which
* can be found in other functions.
*
* @return Returns a double value representing a calculated annual average
* precipitation based on "observed" monthly values found in the top of the
* climate file.
*/
public double annualAvgPrecip() {
if (Double.isNaN(annualAvgPrecip)) {
double avg = 0.0;
for (int i = 0; i < 12; i++) {
avg += observedMmonthlyAverage[i][3];
}
annualAvgPrecip = avg / 12.0;
}
return annualAvgPrecip;
}
public int observedYears() {
return observedYears;
}
public int yearsSimulated() {
return yearsSimulated;
}
public int yearsInFile() {
return yearCount;
}
public int elevation() {
return elevation;
}
public String climateVersion() {
return climateVersion;
}
public int beginningYear() {
return beginningYear;
}
public String stationName() {
return stationName;
}
public double[][] monthlyAverages() {
return monthlyAverage;
}
public double[][] observedMonthlyAverages() {
return observedMmonthlyAverage;
}
public double[] observedMonthlyAverages(int month) {
if ((month >= 0) && (month < 12)) {
return observedMmonthlyAverage[month];
}
return null;
}
public double observedMonthlyAverages(int month, int dataType) {
if ((month >= 0) && (month < 12) && (dataType >= 0) && (dataType < OBS_MONTHLY_LENGTH)) {
return observedMmonthlyAverage[month][dataType];
}
return Double.NaN;
}
public double[] yearlySummary(int yearIndex) {
if ((yearIndex < yearCount) && (yearIndex >= 0)) {
return yearlyData[yearIndex];
}
return null;
}
public double yearlySummary(int yearIndex, int dataType) {
if ((yearIndex < yearCount) && (yearIndex >= 0) && (dataType >= 0) && (dataType < DATA_TYPES_LENGTH)) {
return yearlyData[yearIndex][dataType];
}
return Double.NaN;
}
public double simulationAverage(int dataType) {
if ((dataType >= 0) && (dataType < DATA_TYPES_LENGTH)) {
double total = 0.0;
for (int i = 0; i < 12; i++) {
total += this.monthlyAverage[i][dataType];
}
return (total / 12.0);
}
return Double.NaN;
}
private void readClimateData(String windFileData) throws IOException {
BufferedReader climateData;
climateData = new BufferedReader(new StringReader(windFileData));
String climateLine;
int count = 0;
int febCount = 0;
boolean foundData = false;
boolean foundEndOfCycle = false;
while ((climateLine = climateData.readLine()) != null) {
count++;
if (count == 1) {
climateVersion = climateLine;
continue;
}
if (!foundData) {
if (climateLine.contains("Station:")) {
stationName = climateLine.substring(climateLine.indexOf("Station:") + 9, climateLine.indexOf("CLIGEN") - (climateLine.indexOf("Station:") + 9)).trim();
continue;
}
if (climateLine.contains("Latitude ")) {
climateLine = climateData.readLine();
if (null != climateLine) {
count++;
String[] tokens = climateLine.trim().split("\\s+");
if (tokens.length >= 7) {
elevation = Integer.parseInt(tokens[2]);
observedYears = Integer.parseInt(tokens[3]);
beginningYear = Integer.parseInt(tokens[4]);
yearsSimulated = Integer.parseInt(tokens[5]);
} else {
messages += "\n#\nClimate file is invalid. Data line that follows the headers on line " + count + " is incomplete.";
badClimateData = true;
break;
}
} else {
messages += "\n#\nClimate file is invalid. No data line follows the headers on line " + count;
badClimateData = true;
break;
}
continue;
}
if (climateLine.contains("Observed monthly ave max temperature (C)")) {
climateLine = climateData.readLine();
if (null != climateLine) {
count++;
String[] tokens = climateLine.trim().split("\\s+");
if (tokens.length >= 12) {
for (int i = 0; i < 12; i++) {
observedMmonthlyAverage[i][OBS_MONTHLY_TMAX] = Double.parseDouble(tokens[i]);
}
} else {
messages += "\n#\nClimate file is invalid. Data line that follows the observed monthly average max temperatures header on line " + count + " is incomplete.";
badClimateData = true;
break;
}
} else {
messages += "\n#\nClimate file is invalid. No data line that follows the observed monthly average max temperatures header on line " + count;
badClimateData = true;
break;
}
continue;
}
if (climateLine.contains("Observed monthly ave min temperature (C)")) {
climateLine = climateData.readLine();
if (null != climateLine) {
count++;
String[] tokens = climateLine.trim().split("\\s+");
if (tokens.length >= 12) {
for (int i = 0; i < 12; i++) {
observedMmonthlyAverage[i][OBS_MONTHLY_TMIN] = Double.parseDouble(tokens[i]);
}
} else {
messages += "\n#\nClimate file is invalid. Data line that follows the observed monthly average min temperatures header on line " + count + " is incomplete.";
badClimateData = true;
break;
}
} else {
messages += "\n#\nClimate file is invalid. No data line that follows the observed monthly average min temperatures header on line " + count;
badClimateData = true;
break;
}
continue;
}
if (climateLine.contains("Observed monthly ave solar radiation (Langleys/day)")) {
climateLine = climateData.readLine();
if (null != climateLine) {
count++;
String[] tokens = climateLine.trim().split("\\s+");
if (tokens.length >= 12) {
for (int i = 0; i < 12; i++) {
observedMmonthlyAverage[i][OBS_MONTHLY_RAD] = Double.parseDouble(tokens[i]);
}
} else {
messages += "\n#\nClimate file is invalid. Data line that follows the Observed monthly ave solar radiation (Langleys/day) header on line " + count + " is incomplete.";
badClimateData = true;
break;
}
} else {
messages += "\n#\nClimate file is invalid. No data line that follows the Observed monthly ave solar radiation (Langleys/day) header on line " + count;
badClimateData = true;
break;
}
continue;
}
if (climateLine.contains("Observed monthly ave precipitation (mm)")) {
climateLine = climateData.readLine();
if (null != climateLine) {
count++;
String[] tokens = climateLine.trim().split("\\s+");
if (tokens.length >= 12) {
for (int i = 0; i < 12; i++) {
observedMmonthlyAverage[i][OBS_MONTHLY_PRCP] = Double.parseDouble(tokens[i]);
}
} else {
messages += "\n#\nClimate file is invalid. Data line that follows the Observed monthly ave precipitation (mm) header on line " + count + " is incomplete.";
badClimateData = true;
break;
}
} else {
messages += "\n#\nClimate file is invalid. No data line that follows the Observed monthly ave precipitation (mm) header on line " + count;
badClimateData = true;
break;
}
continue;
}
if (climateLine.contains("da mo year prcp")) {
climateLine = climateData.readLine();
if (null != climateLine) {
count++;
foundData = true;
}
continue;
}
} else {
// If we got to here, then we are now reading the monthly data lines...
int currentYear = -1;
int currentMonth = -1;
int numDays = 0;
int monthDayCount = 0;
yearCount = 0;
int[] runningTotal = new int[DATA_TYPES_LENGTH];
while (true) {
String[] tokens = climateLine.trim().split("\\s+");
if (tokens.length >= DAILY_VALUE_TOKENS) {
int day, month, year;
day = Integer.parseInt(tokens[0]) - 1;
month = Integer.parseInt(tokens[1]) - 1;
year = Integer.parseInt(tokens[2]) - 1;
if (((year >= (MAX_YEARS - 1)) || (yearCount >= yearsSimulated)) && (month == 11) && (day == 30)) {
foundEndOfCycle = true;
}
//Catch month transition and save yearly averages...do this before checking year transition to avoid doing this twice.
if (month != currentMonth) {
if (currentMonth == -1) {
currentMonth = month;
monthDayCount = 0;
} else {
for (int i = 0; i < DATA_TYPES_LENGTH; i++) {
dailyData[currentYear][currentMonth][MONTH_AVERAGE_INDEX][i] /= monthDayCount;
dailyData[MAX_YEAR_AVERAGE_INDEX][currentMonth][MONTH_AVERAGE_INDEX][i] += dailyData[currentYear][currentMonth][MONTH_AVERAGE_INDEX][i];
}
monthDayCount = 0;
currentMonth = month;
}
}
//Catch year transition and save yearly averages.
if (year != currentYear) {
if (currentYear == -1) {
currentYear = year;
for (int i = 0; i < DATA_TYPES_LENGTH; i++) {
runningTotal[i] = 0;
}
} else {
for (int i = 0; i < DATA_TYPES_LENGTH; i++) {
yearlyData[currentYear][i] = runningTotal[i] / numDays;
runningTotal[i] = 0;
}
currentYear = year;
}
numDays = 0;
yearCount++;
}
if (year < MAX_YEARS) {
for (int i = 0; i < DATA_TYPES_LENGTH; i++) {
double dataValue = Double.parseDouble(tokens[i + 3]);
//Set value in matrix for this day/month/year/dataType
dailyData[year][month][day][i] = dataValue;
//Add to MAX_YEAR_AVERAGE_INDEX averages for this day/month/year/dataType
dailyData[MAX_YEAR_AVERAGE_INDEX][month][day][i] += dataValue;
//Add to yearly averages for this dataTypeIndex
runningTotal[i] += dataValue;
//Add to monthly averages for this year for this dataTypeIndex
dailyData[year][month][MONTH_AVERAGE_INDEX][i] += dataValue;
}
// If its February and a leap year, count the number of these that occured in this data set.
if ((month == 1) && (day == 29)) {
febCount++;
}
//Increment number of days of data read this year.
numDays++;
//Increment number of days of data read this month/year
monthDayCount++;
} else {
messages += "\n#\nClimate file was longer than " + MAX_YEARS + " years. Data clipped to " + MAX_YEARS + " years.";
foundEndOfCycle = true;
break;
}
} else {
if (!foundEndOfCycle) {
messages += "\n#\nMissing daily climate data on line " + count + "of the cligen data file. Expected 13 fields and found only " + tokens.length + " fields instead.";
badClimateData = true;
break;
}
}
climateLine = climateData.readLine();
if (null == climateLine) {
break;
}
count++;
}
if (foundEndOfCycle) {
//Get final year's averages since while loop will exit right after filling in the last year of data, but not do that year's averages.
for (int i = 0; i < DATA_TYPES_LENGTH; i++) {
yearlyData[currentYear][i] = runningTotal[i] / numDays;
dailyData[currentYear][currentMonth][MONTH_AVERAGE_INDEX][i] /= monthDayCount;
dailyData[MAX_YEAR_AVERAGE_INDEX][currentMonth][MONTH_AVERAGE_INDEX][i] += dailyData[currentYear][currentMonth][MONTH_AVERAGE_INDEX][i];
}
////////////////////////////////////////////////////////////////////////
// Calculates MAX_YEARS year averages...by day and by month for each dataType.
//
// Also calculate daily averages each month over the MAX_YEARS years...saves a
// daily average for the entire MAX_YEARS years, so the MAX_YEAR_AVERAGE_INDEX line of this matrix contains an average for the entire simulation.
/////////////////////////////////////////////////////////////////////////////////////////////////
for (int monthIndex = 0; monthIndex < 12; monthIndex++) {
double[] monthlyAvg = new double[DATA_TYPES_LENGTH];
int febDayCounter = 0;
// For each day of this month.
for (int monthDay = 0; monthDay < monthDays[monthIndex]; monthDay++) {
// For each data type on this day
for (int dataTypeIndex = 0; dataTypeIndex < DATA_TYPES_LENGTH; dataTypeIndex++) {
if (monthIndex != 1) {
dailyData[MAX_YEAR_AVERAGE_INDEX][monthIndex][monthDay][dataTypeIndex] /= yearsSimulated;
monthlyAvg[dataTypeIndex] += dailyData[MAX_YEAR_AVERAGE_INDEX][monthIndex][monthDay][dataTypeIndex];
} else { // In February
if (monthDay == 28) { // On leap year day 29
dailyData[MAX_YEAR_AVERAGE_INDEX][monthIndex][monthDay][dataTypeIndex] /= febCount;
monthlyAvg[dataTypeIndex] += dailyData[MAX_YEAR_AVERAGE_INDEX][monthIndex][monthDay][dataTypeIndex] * febCount;
febDayCounter += febCount;
} else {
dailyData[MAX_YEAR_AVERAGE_INDEX][monthIndex][monthDay][dataTypeIndex] /= yearsSimulated;
monthlyAvg[dataTypeIndex] += dailyData[MAX_YEAR_AVERAGE_INDEX][monthIndex][monthDay][dataTypeIndex] * 28;
febDayCounter += 28;
}
}
}
}
//Calculate monthly averages over entire yearsSimulated years.
for (int dataTypeIndex = 0; dataTypeIndex < DATA_TYPES_LENGTH; dataTypeIndex++) {
if (monthIndex != 1) {
monthlyAverage[monthIndex][dataTypeIndex] = monthlyAvg[dataTypeIndex] / monthDays[monthIndex];
} else {
monthlyAverage[monthIndex][dataTypeIndex] = monthlyAvg[dataTypeIndex] / febDayCounter;
}
// This value should match the one saved just above...check on this...
dailyData[MAX_YEAR_AVERAGE_INDEX][monthIndex][MONTH_AVERAGE_INDEX][dataTypeIndex] /= yearsSimulated;
}
}
//Done with this file...
break;
} else {
messages += "\n#\nDid not find an entire " + MAX_YEARS + " year simulation of climate data.";
badClimateData = true;
break;
}
}
}
}
}