/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package oms3.dsl.cosu;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Random;
import ngmf.util.cosu.luca.of.ABSDIF;
import ngmf.util.cosu.luca.of.ABSDIFLOG;
import ngmf.util.cosu.luca.of.AVE;
import ngmf.util.cosu.luca.of.BIAS;
import ngmf.util.cosu.luca.of.IOA;
import ngmf.util.cosu.luca.of.IOA2;
import ngmf.util.cosu.luca.of.NS;
import ngmf.util.cosu.luca.of.NS2LOG;
import ngmf.util.cosu.luca.of.NSLOG;
import ngmf.util.cosu.luca.of.PMCC;
import ngmf.util.cosu.luca.of.RMSE;
import ngmf.util.cosu.luca.of.TRMSE;
import oms3.ObjectiveFunction;
import oms3.SimConst;
import oms3.dsl.Buildable;
import oms3.io.CSTable;
import oms3.io.DataIO;
/**
* Objective function handling.
*
* @author od
*/
public class ObjFunc implements Buildable {
static final List<String> months = Arrays.asList("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
static final boolean showCacheStats = false;
static final int checkCachePercentage = 5;
static int usedCache = 0;
static int readData = 0;
//
double weight = Double.NaN;
String timestep = SimConst.DAILY;
ObjectiveFunction of;
//
CSVColumn sim;
CSVColumn obs;
CSVColumn sd = null;
//
String method;
private boolean enableCache = false; // default.
private boolean cache_valid = false;
private double[] cache = new double[1];
boolean[] periodMask = null;
private int subDivideValue = Integer.MIN_VALUE;
boolean[] subDivideMask = null;
double invalidData = -90.0;
public CSVColumn getSimulated() {
if (sim == null) {
throw new IllegalArgumentException("Missing 'sim' argument");
}
return sim;
}
public CSVColumn getObserved() {
if (obs == null) {
throw new IllegalArgumentException("Missing 'obs' argument");
}
return obs;
}
@Override
public Buildable create(Object name, Object value) {
if (name.equals("sim")) {
return sim = new CSVColumn();
} else if (name.equals("obs")) {
return obs = new CSVColumn();
} else if (name.equals("subdivide")) {
return sd = new CSVColumn();
}
throw new IllegalArgumentException(name.toString());
}
public void setMethod(String method) {
this.method = method;
if (method.equals(SimConst.ABSDIF)) {
of = new ABSDIF();
} else if (method.equals(SimConst.ABSDIFLOG)) {
of = new ABSDIFLOG();
} else if (method.equals(SimConst.AVE)) {
of = new AVE();
} else if (method.equals(SimConst.IOA)) {
of = new IOA();
} else if (method.equals(SimConst.IOA2)) {
of = new IOA2();
} else if (method.equals(SimConst.NS)) {
of = new NS();
} else if (method.equals(SimConst.NSLOG)) {
of = new NSLOG();
} else if (method.equals(SimConst.NS2LOG)) {
of = new NS2LOG();
} else if (method.equals(SimConst.NBIAS)) {
of = new BIAS();
} else if (method.equals(SimConst.PMCC)) {
of = new PMCC();
} else if (method.equals(SimConst.RMSE)) {
of = new RMSE();
} else if (method.equals(SimConst.TRMSE)) {
of = new TRMSE();
} else {
try {
// load this as a class name from default classpath
Class<?> c = Class.forName(method);
of = (ObjectiveFunction) c.newInstance();
} catch (Exception E) {
throw new IllegalArgumentException("No such method: " + method);
}
}
}
public String getMethod() {
return method;
}
public void setTimestep(String timestep) {
if ((!timestep.equals(SimConst.DAILY))
&& (!timestep.equals(SimConst.DAILY_MEAN))
&& (!timestep.equals(SimConst.MONTHLY_MEAN))
&& (!timestep.equals(SimConst.MEAN_MONTHLY))
&& (!timestep.equals(SimConst.ANNUAL_MEAN))
&& (!timestep.equals(SimConst.PERIOD_MAXIMUM))
&& (!timestep.equals(SimConst.PERIOD_MININUM))
&& (!timestep.equals(SimConst.PERIOD_MEDIAN))
&& (!timestep.equals(SimConst.PERIOD_STANDARD_DEVIATION))) {
throw new IllegalArgumentException("SetTimeStep: Illegal timestep: " + timestep);
}
this.timestep = timestep;
}
public void setInvalidDataValue(String invalidDataValue) {
this.invalidData = Double.parseDouble(invalidDataValue);
// System.out.println("Using " + this.invalidData + "as invalid data value.");
}
public void setSubdivide_value(String subdivide_value) {
this.subDivideValue = Integer.parseInt(subdivide_value);
}
public int getSubDivideValue() {
return this.subDivideValue;
}
public void setSubDivideMask(boolean[] in) {
this.subDivideMask = in;
}
public String getSubDivideString() {
if (sd == null) {
return null;
} else {
return "File: " + sd.getFile() + ", Table: " + sd.getTable() + ", Column: " + sd.getColumn();
}
}
public boolean[] getSubDivideMask(CSVColumn sdi, Date start, Date end, File folder) {
if (sdi != null) {
try {
File fname = ObjFunc.resolve(sdi.getFile(), folder);
CSTable sdt = DataIO.table(fname, sdi.getTable());
String column = sdi.getColumn();
double[] retData = DataIO.getColumnDoubleValuesInterval(start, end, sdt, column, DataIO.DAILY, 0, null, null);
int[] subDivideData = new int[retData.length];
for (int i = 0; i < retData.length; i++) {
subDivideData[i] = (int) retData[i];
}
if ((subDivideMask == null) && (subDivideData != null)) {
this.subDivideMask = new boolean[subDivideData.length];
for (int i = 0; i < subDivideData.length; i++) {
this.subDivideMask[i] = ((subDivideData[i] == this.getSubDivideValue()) || (this.getSubDivideValue() == Integer.MIN_VALUE));
}
}
return this.subDivideMask;
} catch (IOException E) {
throw new RuntimeException(E);
}
} else {
return null;
}
}
public void setEnableCache(String enableCache) {
if (enableCache.equals("true") || enableCache.equals("t") || enableCache.equals("1")) {
this.enableCache = true;
} else {
this.enableCache = false;
}
if (showCacheStats) {
String cstr = this.enableCache ? "enabled" : "disabled";
System.out.println("Cache is " + cstr + " for ObjFunc with method = " + this.getMethod());
}
}
public void setPeriodRange(String periodRange) {
System.out.println("*****Setting Period Range to " + periodRange);
parseRange pr = new parseRange();
boolean[] range_tmp = pr.getArray(periodRange, 13);
this.periodMask = new boolean[12];
for (int i = 0; i < 12; i++) {
this.periodMask[i] = range_tmp[i + 1]; // convert from 1-12 to 0-13.
}
}
public boolean[] getPeriodMask() {
return this.periodMask;
}
public String getPeriodStartName(int start) {
return months.get(start);
}
public String getPeriodEndName(int start) {
int periodEnd = (start + 11) % 12;
return months.get(periodEnd);
}
public void setWeight(double weight) {
if (weight <= 0 || weight > 1) {
throw new IllegalArgumentException("of weight out of range: " + weight);
}
this.weight = weight;
}
String getTimestep() {
return timestep;
}
double getWeight() {
return weight;
}
ObjectiveFunction getOF() {
if (of == null) {
throw new IllegalArgumentException("No Objective function method defined.");
}
return of;
}
// static
public static boolean isInc(List<ObjFunc> ofs) {
if (ofs.isEmpty()) {
throw new IllegalArgumentException("No Objective function(s) defined. ");
}
boolean inc = ofs.get(0).getOF().positiveDirection();
for (ObjFunc of : ofs) {
if (of.getOF().positiveDirection() != inc) {
throw new IllegalArgumentException("Objective function(s) optimization direction mismatch!");
}
}
return inc;
}
public static void adjustWeights(List<ObjFunc> ofs) {
int noOf = ofs.size();
for (ObjFunc of : ofs) {
if (Double.isNaN(of.getWeight())) {
of.setWeight((double) 1 / noOf);
}
}
}
private void setcache(double[] data) {
this.cache = data;
this.cache_valid = this.enableCache;
}
private double[] getCacheData() {
return this.cache;
}
private Boolean getCacheValid() {
return this.cache_valid;
}
public static void checkCache(double[] cache, double[] read) {
boolean error = false;
if (cache.length != read.length) {
System.out.println("\tERROR: Cache array length difference : cache length = " + cache.length
+ "\t obsval length = " + read.length);
error = true;
}
for (int i = 0; i < cache.length; i++) {
if (cache[i] != read[i]) {
System.out.println("\tERROR: Data " + i + "mismatch: cache = " + cache[i] + "\t computed = " + read[i]);
error = true;
}
}
if (error == true) {
throw new RuntimeException("\tERROR: !!!Check on validity of observed cache data check showed an Error!!!");
}
error = false;
if (showCacheStats) {
System.out.print("\n\tRandom cache data validity check passed.\t");
}
}
public static double[] getObs(ObjFunc of, Date start, Date end, CSTable tobs, String columnName, int timeStep, int startMonth,
boolean[] periodMask, boolean[] sdMask) {
double[] obsval;
// System.out.println("CacheValid = " + of.getCacheValid());
if (of.getCacheValid()) {
obsval = of.getCacheData();
if (showCacheStats) {
usedCache++;
}
// Randomally check data
Random r = new Random();
if (r.nextInt(100) < checkCachePercentage) {
//System.out.print(" Verifying cache data validity");
double[] obsval_orig = DataIO.getColumnDoubleValuesInterval(start, end, tobs, columnName, timeStep, startMonth, periodMask, sdMask);
checkCache(obsval, obsval_orig);
}
} else {
obsval = DataIO.getColumnDoubleValuesInterval(start, end, tobs, columnName, timeStep, startMonth, periodMask, sdMask);
if (showCacheStats) {
readData++;
}
}
of.setcache(obsval);
return obsval;
}
public static double calculateObjectiveFunctionValue(List<ObjFunc> ofs, Date start, Date end, File folder) {
return calculateObjectiveFunctionValue(ofs, 0, start, end, folder);
}
public static double calculateObjectiveFunctionValue(List<ObjFunc> ofs, int startMonthOfYear, Date start, Date end, File folder) {
try {
if (ofs.isEmpty()) {
throw new IllegalArgumentException("No Objective function(s) defined. ");
}
double val = 0.0;
double weight = 0.0;
adjustWeights(ofs);
for (ObjFunc of : ofs) {
CSVColumn obs = of.getObserved();
String timeStepString = of.getTimestep();
int timeStep = DataIO.DAILY;
if (timeStepString.equals(SimConst.DAILY)) {
timeStep = DataIO.DAILY;
} else if (timeStepString.equals(SimConst.MEAN_MONTHLY)) {
timeStep = DataIO.MEAN_MONTHLY;
} else if (timeStepString.equals(SimConst.MONTHLY_MEAN)) {
timeStep = DataIO.MONTHLY_MEAN;
} else if (timeStepString.equals(SimConst.ANNUAL_MEAN)) {
timeStep = DataIO.ANNUAL_MEAN;
} else if (timeStepString.equals(SimConst.PERIOD_MEAN)) {
timeStep = DataIO.PERIOD_MEAN;
} else if (timeStepString.equals(SimConst.PERIOD_MEDIAN)) {
timeStep = DataIO.PERIOD_MEDIAN;
} else if (timeStepString.equals(SimConst.PERIOD_MININUM)) {
timeStep = DataIO.PERIOD_MIN;
} else if (timeStepString.equals(SimConst.PERIOD_MAXIMUM)) {
timeStep = DataIO.PERIOD_MAX;
} else if (timeStepString.equals(SimConst.PERIOD_STANDARD_DEVIATION)) {
timeStep = DataIO.PERIOD_STANDARD_DEVIATION;
} else {
throw new IllegalArgumentException("TimeStep " + timeStepString + "unknown.");
}
boolean[] pMask = of.getPeriodMask();
boolean[] sdMask = of.getSubDivideMask(of.sd, start, end, folder);
CSTable tobs = DataIO.table(resolve(obs.getFile(), folder), obs.getTable());
String column = obs.getColumn();
double[] obsval = getObs(of, start, end, tobs, column, timeStep, startMonthOfYear, pMask, sdMask);
CSVColumn sim = of.getSimulated();
CSTable tsim = DataIO.table(resolve(sim.getFile(), folder), sim.getTable());
double[] simval = DataIO.getColumnDoubleValuesInterval(start, end, tsim, sim.getColumn(), timeStep, startMonthOfYear, pMask, sdMask);
weight += of.getWeight();
val += of.getOF().calculate(obsval, simval, of.invalidData) * of.getWeight();
}
if (showCacheStats) {
double cacheUse = ((double) usedCache) / ((double) usedCache + (double) readData) * 100;
int cacheUseInt = (int) cacheUse;
System.out.print("\t" + cacheUseInt + "% cache use : Read Data " + readData + " times. Used Cache Data " + usedCache + " times");
}
if (weight != 1.0) {
throw new IllegalArgumentException("sum of of weights != 1.0");
}
return val;
} catch (IOException E) {
throw new RuntimeException(E);
}
}
public static File resolve(String file, File out) {
File f = new File(file);
if (!(f.isAbsolute() && f.exists())) {
f = new File(out, file);
}
if (!f.exists()) {
throw new IllegalArgumentException("File not found: " + file);
}
return f;
}
}