V1_0.java [src/java/m/oms/ages] Revision: 950e80ac258d922e7a761fe3301808fd13241bb5  Date: Sat Mar 21 19:24:58 MDT 2020
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package m.oms.ages;

import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.*;
import static csip.annotations.ResourceType.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.Path;
import static m.oms.ages.V1_0.KEY_SCRIPT;
import ngmf.util.cosu.luca.of.KGE;
import ngmf.util.cosu.luca.of.NS;
import ngmf.util.cosu.luca.of.NS2LOG;
import ngmf.util.cosu.luca.of.RMSE;
import ngmf.util.cosu.luca.of.TRMSE;
import oms.utils.Utils;
import oms3.ObjectiveFunction;

/**
 * Ages service.
 *
 * @author od
 */
@Name("AGES model execution")
@Description("OMS based AGES model service")
@VersionInfo("1.0")
@Path("m/ages/1.0")
@Resource(file = "/bin/ages/ages.jar", type = JAR, id = Utils.ID_AGES_JAR)
@Resource(file = "/bin/ages/ages-lib.zip", type = ARCHIVE)
@Resource(file = "/bin/ages/ages-static.zip", type = ARCHIVE)
@Resource(file = "java-*-std*.txt output/csip_run/out/*.csv", type = OUTPUT)
@Resource(file = "${csip.dir}/bin/ages/simulation/ages.sim", type = REFERENCE, id = KEY_SCRIPT)
public class V1_0 extends ModelDataService {

  public static final String KEY_SCRIPT = "ages.sim";
  public static final String RUN_INC = "run.inc";

  public static final String PAR_STARTTIME = "startTime";
  public static final String PAR_ENDTIME = "endTime";

  static Map<String, ObjectiveFunction> OF = new HashMap<>();

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


  static {
    OF.put("kge", new KGE());
    OF.put("ns", new NS());
    OF.put("nslog", new NS2LOG());
    OF.put("rmse", new RMSE());
    OF.put("trmse", new TRMSE());
    OF.put("pbias", new PBIAS());
  }

  // required parameter
  String[] reqParams = {
    PAR_STARTTIME,
    PAR_ENDTIME
  };

  // optional parameter
  String[] optParams = {
    "ACAdaptation",
    "BetaW",
    "Beta_min",
    "Beta_rsd",
    "FCAdaptation",
    "Ksink",
    "LExCoef",
    "N_delay_RG1",
    "N_delay_RG2",
    "a_rain",
    "a_snow",
    "angstrom_a",
    "angstrom_b",
    "baseTemp",
    "delayNitrification",
    "denitrificationRateCoefficient",
    "denitrificationSoilSaturationThreshold",
    "nitrificationSoilTemperatureThreshold",
    "nitrificationSurfaceTemperatureThreshold",
    "calib_clat_fact",
    "ccf_factor",
    "defaultCO2",
    "denitfac",
    "depdr",
    "deposition_factor",
    "drrad",
    "drspac",
    "f1",
    "f12",
    "f13",
    "f14",
    "f15",
    "f16",
    "f17",
    "f18",
    "f19",
    "f2",
    "f3",
    "f4",
    "f5",
    "f6",
    "f7",
    "f8",
    "flowRouteTA",
    "fmt_date",
    "fmt_double",
    "g_factor",
    "geoMaxPerc",
    "gwCapRise",
    "gwRG1Fact",
    "gwRG1RG2dist",
    "gwRG2Fact",
    "halflife_RG1",
    "halflife_RG2",
    "infil_conc_factor",
    "initLPS",
    "initMPS",
    "initN_concRG1",
    "initN_concRG2",
    "initRG1",
    "initRG2",
    "kdiff_layer",
    "kf_calib",
    "lagSurfaceRunoff",
    "lagInterflow",
    "locGrw",
    "longTZ",
    "nitri_delay",
    "opti",
    "piadin",
    "r_factor",
    "rootfactor",
    "sceno",
    "skipRegression",
    "snowCritDens",
    "snowDensConst",
    "snowFactorA",
    "snowFactorB",
    "snowFactorC",
    "snow_trans",
    "snow_trs",
    "soilDiffMPSLPS",
    "soilDistMPSLPS",
    "soilImpGT80",
    "soilImpLT80",
    "soilLatVertLPS",
    "soilLinRed",
    "soilMaxDPS",
    "soilMaxInfSnow",
    "soilMaxInfSummer",
    "soilMaxInfWinter",
    "soilMaxPerc",
    "soilOutLPS",
    "soilPolRed",
    "t_factor",
    "tempRes",
    "temp_lag",
  };

  // simulation flags
  String[] flags = {
    "flagParallel",
    "flagRegionalization",
    "flagHRURouting",
    "flagReachRouting",
    "flagSort",
    "flagSplit",
    "flagInfiltration",
    "flagTillage",
    "flagTileDrain",
    "flagUPGM",
    "parallelismFactor",
    "parallelismThreads",
    "flagSaveState",
    "flagLoadState",
    "flagWB"
  };


  @Override
  public void doProcess() throws Exception {
    String dsl = parameter().getString(KEY_SCRIPT,
        resources().getFile(KEY_SCRIPT).toString());

    // pass request param to model runtime parameter -> run.inc
    Map<String, String> agesParam = new LinkedHashMap<>();
    Utils.passReqQuotedParam(agesParam, parameter(), reqParams);

    // scalar parameter
    Utils.passOptParam(agesParam, parameter(), optParams);

    // optional flags
    Utils.passOptQuotedParam(agesParam, parameter(), flags);

    // create parameter include file
    Utils.createParamInclude(agesParam, getWorkspaceFile(RUN_INC));

    String start = parameter().getString(PAR_STARTTIME);
    String end = parameter().getString(PAR_ENDTIME);

    File d = new File(dsl);
    if (!(d.isAbsolute() && d.exists())) {
      d = getWorkspaceFile(dsl);
    }

    getWorkspaceFile("output").mkdirs();
    getWorkspaceFile("logs").mkdirs();

    Utils.runAges(d, getWorkspaceDir(), parameter(), resources(), LOG);

    for (String ofName : getRequestedObjfunc(OF.keySet())) {
      String[] data = parameter().getStringArray(ofName);
      double v = calc_of(OF.get(ofName), data[0], data[1], start, end);
      results().put(ofName, v);
    }
//  results().put(getWorkspaceFile("output"));
  }


  private List<String> getRequestedObjfunc(Set<String> names) {
    List<String> l = new ArrayList<>();
    for (String ofName : names) {
      if (parameter().has(ofName)) {
        l.add(ofName);
      }
    }
    return l;
  }


  private double calc_of(ObjectiveFunction of, String obs,
      String sim, String start, String end) throws IOException, ServiceException {
    // e.g. obs_data02_14.csv/obs/orun[1]
    double[] obsData = Utils.getData(obs, getWorkspaceDir(), start, end);
    // e.g. output/csip_run/out/Outlet.csv/output/catchmentSimRunoff
    double[] simData = Utils.getData(sim, getWorkspaceDir(), start, end);
    return of.calculate(obsData, simData, parameter().getDouble("missing", -9999d));
  }

}