Utils.java [src/java/oms/utils] Revision: d0f64f5b837988cebd5b95d68a07aa34e17b70b6  Date: Thu Apr 15 15:50:09 MDT 2021
/*
 * 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 oms.utils;

import csip.Executable;
import csip.PayloadParameter;
import csip.ServiceException;
import csip.ServiceResources;
import csip.SessionLogger;
import csip.utils.Binaries;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import oms3.Conversions;
import oms3.ObjectiveFunction;
import oms3.io.CSTable;
import oms3.io.DataIO;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.lang.ArrayUtils;

/**
 *
 * @author od
 */
public class Utils {

  // parameter keys
  public static final String KEY_LOGLEVEL = "loglevel";
  public static final String KEY_OPTIONS = "java.options";

  public static final String ID_AGES_JAR = "ages.jar";

  /**
   *
   */
  public static class PBIAS implements ObjectiveFunction {

    @Override
    public double calculate(double[] obs, double[] sim, double missing) {
      if (sim.length != obs.length) {
        throw new IllegalArgumentException("obs/sim length differ: " + obs.length + "!=" + sim.length);
      }
      double diffsum = 0;
      double obssum = 0;
      for (int i = 0; i < sim.length; i++) {
        if (obs[i] > missing) {
          diffsum += sim[i] - obs[i];
          obssum += obs[i];
        }
      }
      return (diffsum / obssum) * 100.0;
    }


    @Override
    public boolean positiveDirection() {
      return false;
    }
  }

  /**
   *
   */
  public static class NSLOG1P implements ObjectiveFunction {

    @Override
    public double calculate(double[] obs, double[] sim, double missing) {
      if (sim.length != obs.length) {
        throw new IllegalArgumentException("obs/sim length differ: " + obs.length + "!=" + sim.length);
      }

      /**
       * calculating logarithmic values of both data sets. Sets 0 if data is 0
       */
      int valid = 0;
      double avg = 0.0;
      for (int i = 0; i < sim.length; i++) {
        if (sim[i] >= 0.0 && obs[i] >= 0.0) {
          // summing up 
          avg += Math.log1p(obs[i]);
          valid++;
        }
      }

      if (valid < 2) {
        return Double.NEGATIVE_INFINITY;
      }

      // calculating mean 
      avg /= valid;

      // calculating mean pow deviations
      double rmse = 0.0;
      double e = 0.0;
      for (int i = 0; i < sim.length; i++) {
        if (sim[i] >= 0 && obs[i] >= 0) {
          double l1po = Math.log1p(obs[i]);
          rmse += Math.pow(Math.abs(l1po - Math.log1p(sim[i])), 2);
          e += Math.pow(Math.abs(l1po - avg), 2);
        }
      }
      double r = 1 - (rmse / e);
      return Double.isNaN(r) ? 0.0 : r;
    }


    @Override
    public boolean positiveDirection() {
      return true;
    }
  }

  /**
   *
   */
  public static class NSLOG2 implements ObjectiveFunction {

    @Override
    public double calculate(double obs[], double sim[], double missing) {
      if (obs.length != sim.length) {
        throw new IllegalArgumentException("obs/sim length differ: " + obs.length + "!=" + sim.length);
      }
      double pow = 2;
      double rsme = 0;
      double var = 0;
      double avg = 0;
      double count = 0;
      for (int i = 0; i < obs.length; i++) {
        if (obs[i] > 0 && obs[i] != missing) {
          avg += Math.log(obs[i]);
          count += 1;
        }
      }
      avg /= count;

      for (int i = 0; i < obs.length; i++) {
        if (obs[i] > 0 && sim[i] > 0 && obs[i] != missing) {
          rsme += Math.pow(Math.abs(Math.log(obs[i]) - Math.log(sim[i])), pow);
          var += Math.pow(Math.abs(Math.log(obs[i]) - avg), pow);
        }
      }
      double result = 1.0 - (rsme / var);
      if (Double.isNaN(result)) {
        result = 0;
      }
      return result;
    }


    @Override
    public boolean positiveDirection() {
      return true;
    }
  }


  /**
   * create a 'sim' include file for the run part.
   */
  public static void createParamInclude(Map<String, String> p,
      File file) throws IOException {
    StringBuilder b = new StringBuilder();
    b.append("parameter {\n");
    p.keySet().forEach((name) -> {
      b.append("  ").append(name).append(" ").append(p.get(name)).append("\n");
    });
    b.append("}\n");
    FileUtils.writeStringToFile(file, b.toString());
  }


  /**
   * pass a required parameter, quoted (string).
   */
  public static void passReqQuotedParam(Map<String, String> p,
      PayloadParameter param, String... names) throws ServiceException {
    for (String name : names) {
      p.put(name, "\"" + param.getString(name) + "\"");
    }
  }


  /**
   * pass optional parameter, no quotes.
   */
  public static void passOptParam(Map<String, String> p,
      PayloadParameter param, String... names) throws ServiceException {
    for (String name : names) {
      if (param.has(name)) {
        p.put(name, param.getString(name));
      }
    }
  }


  public static void passOptQuotedParam(Map<String, String> p,
      PayloadParameter param, String... names) throws ServiceException {
    for (String name : names) {
      if (param.has(name)) {
        p.put(name, "\"" + param.getString(name) + "\"");
      }
    }
  }


  /**
   * Run Ages
   *
   * @param dsl
   * @param options
   * @throws Exception
   */
  public static void runAges(File dsl, File ws, PayloadParameter param,
      ServiceResources res, SessionLogger LOG) throws Exception {

    // Create/execute a Ages.
    Executable p = createProcess(dsl, ws, param, res, LOG);
    int result = p.exec();
    if (result != 0) {
      File f = p.stderr();
      if (f.exists() && f.length() > 0) {
        String err = FileUtils.readFileToString(f, "UTF-8");
        LOG.info("Ages execution error. " + f + ":\n" + err);
        throw new ServiceException("Ages execution error. " + f + ":\n" + err);
      }
      throw new ServiceException("Ages execution error." + result);
    }
  }


  /**
   * Create the external Ages process.
   */
  public static Executable createProcess(File dsl, File ws,
      PayloadParameter param, ServiceResources res, SessionLogger LOG) throws Exception {

    Map<String, String> sysprops = new HashMap();
    sysprops.put("oms_prj", ws.toString());
    sysprops.put("csip_ages", res.getFile(ID_AGES_JAR).getParent());

    String[] jvmOptions = Binaries.asSysProps(sysprops);
    String options = param.getString(KEY_OPTIONS, "");
    if (options != null && !options.isEmpty()) {
      jvmOptions = (String[]) ArrayUtils.addAll(jvmOptions, options.split("\\s+"));
    }

    // java -Doms_prj=. -cp "dist/AgES.jar" oms3.CLI -l OFF -r "projects/sfir30/simulation/sfir30.sim"
    return Binaries.getResourceOMSDSL(
        dsl, // the dsl file to run
        jvmOptions, // jvm options
        ws, // workspace dir
        Arrays.asList(res.getFile(ID_AGES_JAR)), // the ages jar file 
        param.getString(KEY_LOGLEVEL, "INFO"), // The log level
        LOG); // This session logger
  }

  private static final int FILE = 0;
  private static final int TABLE = 1;
  private static final int COLUMN = 2;


  /**
   *
   * @param d
   * @param workspace
   * @param start
   * @param end
   * @return
   * @throws IOException
   */
  public static double[] getData(String d, File workspace,
      String start, String end) throws IOException {
    if (d == null) {
      throw new IllegalArgumentException("Missing data property: " + d);
    }
    String[] parts = DataIO.parseCsvFilename(d);
    if (parts.length != 3) {
      throw new IllegalArgumentException("invalid: " + d + " expected:: <file>/<table>/<column>");
    }

//    System.out.println(Arrays.toString(parts));
    CSTable t = DataIO.table(new File(workspace, parts[FILE]), parts[TABLE]);

    Date startDate = Conversions.convert(start, Date.class);
    Date endDate = Conversions.convert(end, Date.class);

//    System.out.println(startDate);
//    System.out.println(endDate);
    double[] vals = DataIO.getColumnDoubleValuesInterval(startDate, endDate, t,
        parts[COLUMN], DataIO.DAILY);

    return vals;
  }

//  static void d() throws IOException {
//    CSProperties pr = DataIO.properties(new File("/od/projects/csip-all/csip-oms/tmp/data/main_params.csv"), "Parameter");
//    List<String> l = new ArrayList<>(pr.keySet()) ;       
//    Collections.sort(l);
//    for (String string : l) {
//      System.out.println("\"" + string + "\",");
//    }
//  }

  public static void main(String[] args) throws IOException {
    // small test
    double[] d = getData("obs_data02_14.csv/obs/orun[1]", new File("/tmp"), "2002-01-18", "2002-02-18");
    System.out.println(Arrays.toString(d));
//    d();
  }

}