ObjFunc.java [src/csip/cosu] Revision: Date:
/*
* $Id$
*
* This file is part of the Cloud Services Integration Platform (CSIP),
* a Model-as-a-Service framework, API and application suite.
*
* 2012-2022, Olaf David and others, OMSLab, Colorado State University.
*
* OMSLab licenses this file to you under the MIT license.
* See the LICENSE file in the project root for more information.
*/
package csip.cosu;
import csip.utils.SimpleCache;
import java.util.Arrays;
import java.util.List;
/**
* Objective Functions.
*
* @author od
*/
public abstract class ObjFunc {
/**
* The name of the OF.
*
* @return the OF name
*/
abstract public String name();
/**
* evaluate the OF
*
* @param obs observed values
* @param sim simulated values
* @param missing missing value
* @return the of value
*/
abstract public double eval(double[] obs, double[] sim, double missing);
/**
* In which direction is the OF progressing (1 positive, -1 negative
* direction)
*
* @return the direction of improvement
*/
abstract public int direction();
/**
* What is the optimal OF optimum value.
*
* @return the target value
*/
abstract public int optimum();
/**
* Nash-Sutcliffe Efficiency
*/
public static final String NS = "ns";
/**
* Modified Nash-Sutcliffe Efficiency
*/
public static final String MNS = "mns";
/**
* Klinge-Gupta Efficiency; 2012
*/
public static final String KGE = "kge";
/**
* Klinge-Gupta Efficiency; 2009
*/
public static final String KGE09 = "kge09";
public static final String NSLOG1P = "nslog1p";
public static final String NSLOG2 = "nslog2";
public static final String NS2LOG = "ns2log";
/**
* Transformed RMSE
*/
public static final String TRMSE = "trmse";
/*
* Fenicia high flow
*/
public static final String FHF = "fhf";
/*
* Fenicia low flow
*/
public static final String FLF = "flf";
/**
* Percent Bias
*/
public static final String PBIAS = "pbias";
/**
* Bias
*/
public static final String BIAS = "bias";
/**
* Root Mean Square Error
*/
public static final String RMSE = "rmse";
/**
* Mean Square Error
*/
public static final String MSE = "mse";
public static final String PMCC = "pmcc";
/**
* Index of Agreement
*/
public static final String IOA = "ioa";
public static final String IOA2 = "ioa2";
/**
* Average Volume Error
*/
public static final String AVE = "ave";
/**
* Absolute Difference
*/
public static final String ABSDIFF = "absdiff";
/**
* Log of Absolute Difference
*/
public static final String ABSDIFFLOG = "absdifflog";
private static final List<String> OF_LIST
= Arrays.asList(KGE, NS, MNS, NSLOG1P, NSLOG2, NS2LOG, RMSE, TRMSE, MSE, PBIAS, FLF, FHF, BIAS, PMCC, IOA, IOA2, AVE, ABSDIFF, ABSDIFFLOG, KGE09);
private static final SimpleCache<String, ObjFunc> OFS = new SimpleCache<>();
/**
* Get an OF instance.
*
* @param name the OF name to get .
* @return The OF instance
*/
public static ObjFunc of(String name) {
return OFS.get(name, o -> create(name, null));
}
/**
* Fetch an OF instance or supply your own OF
*
* @param name the OF name to get .
* @param f a custom OF if the name fails to resolve
* @return the OF instance.
*/
public static ObjFunc of(String name, ObjFunc f) {
return OFS.get(name, o -> create(name, f));
}
/**
* List available OFs.
*
* @return the list of registered OF names
*/
public static List<String> list() {
return OF_LIST;
}
///////////////
private static ObjFunc create(String name, ObjFunc f) {
switch (name.toLowerCase().trim()) {
case KGE:
return new KGE();
case NS:
return new NS();
case MNS:
return new MNS();
case KGE09:
return new KGE09();
case NS2LOG:
return new NS2LOG();
case NSLOG1P:
return new NSLOG1P();
case NSLOG2:
return new NSLOG2();
case MSE:
return new MSE();
case RMSE:
return new RMSE();
case TRMSE:
return new TRMSE();
case PBIAS:
return new PBIAS();
case BIAS:
return new BIAS();
case PMCC:
return new PMCC();
case IOA:
return new IOA();
case IOA2:
return new IOA2();
case FHF:
return new FHF();
case FLF:
return new FLF();
case AVE:
return new AVE();
case ABSDIFF:
return new ABSDIFF();
case ABSDIFFLOG:
return new ABSDIFFLOG();
default:
if (f != null) {
return f;
} else {
throw new IllegalArgumentException(name);
}
}
}
/**
* Check if the arrays have the same length.
*
* @param sim the simulated values
* @param obs the observed values
* @return the array len
*/
static int checkArrays(double[] sim, double[] obs) {
if (obs == null || obs.length == 0) {
throw new IllegalArgumentException("obs null or empty.");
}
if (sim == null || sim.length == 0) {
throw new IllegalArgumentException("sim null or empty.");
}
if (obs.length != sim.length) {
throw new IllegalArgumentException("obs/sim not same size: (" + obs.length + "!=" + sim.length + ")");
}
return obs.length;
}
static double mse(double[] obs, double[] sim, double missing) {
checkArrays(obs, sim);
double error = 0;
int len = 0;
for (int i = 0; i < sim.length; i++) {
if (obs[i] > missing) {
double diff = obs[i] - sim[i];
error += diff * diff;
len++;
}
}
if (len == 0) {
throw new RuntimeException("No valid len value.");
}
error /= len;
return error;
}
static double ioa(double[] obs, double[] sim, double pow, double missing) {
checkArrays(obs, sim);
int steps = sim.length;
double sum_obs = 0;
double len = 0.0;
for (int i = 0; i < steps; i++) {
if (obs[i] > missing) {
sum_obs += obs[i];
len++;
}
}
if (len == 0) {
throw new RuntimeException("No valid len value.");
}
// calculating mean values for both data sets
double mean_obs = sum_obs / len;
// calculating absolute squared sum of deviations from verification mean
double td_vd = 0;
double abs_sqDevi = 0;
for (int i = 0; i < steps; i++) {
if (obs[i] > missing) {
td_vd += (Math.pow((Math.abs(obs[i] - sim[i])), pow));
abs_sqDevi += Math.pow(Math.abs(sim[i] - mean_obs) + Math.abs(obs[i] - mean_obs), pow);
}
}
if (Double.compare(abs_sqDevi, 0.0) == 0) {
throw new RuntimeException("No valid abs_sqDevi value.");
}
return 1.0 - (td_vd / abs_sqDevi);
}
static double nbias(double[] obs, double[] sim, double missing) {
checkArrays(sim, obs);
double sum = 0;
double sum1 = 0;
for (int i = 0; i < sim.length; i++) {
if (obs[i] > missing) {
sum += sim[i] - obs[i];
sum1 += obs[i];
}
}
if (Double.compare(sum, 0.0) == 0) {
throw new RuntimeException("No valid sum value.");
}
return sum / sum1;
}
}