V2_0.java [src/java/m/hydrotools/efh2] Revision: default Date:
/*
* 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 m.hydrotools.efh2;
import csip.Executable;
import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.*;
import static csip.annotations.ResourceType.EXECUTABLE;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.ws.rs.Path;
import org.apache.commons.io.FileUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
/**
* TR20 Service, efh2 version.
*
* @author od
*/
@Name("Hydrotools")
@Description("EFH2 service based on TR20")
@Path("m/efh2/2.0")
@Resource(file = "/bin/lin-amd64/TR20.exe", type = EXECUTABLE, id = "tr20")
@Polling(first = 10000, next = 2000)
public class V2_0 extends ModelDataService {
// parameter
static final String WATERSHED_NAME = "watershed_name";
static final String DRAINAGE_AREA = "drainage_area";
static final String CURVE_NUMBER = "curve_number";
static final String WATERSHED_SLOPE = "watershed_slope";
static final String WATERSHED_LENGTH = "watershed_length";
static final String STORM_NAME = "storm_name";
static final String RAIN_DEPTH = "rain_depth";
static final String RAIN_DIST = "rain_dist";
// output
static final String RUNOFF_VOLUME = "runoff_volume";
static final String PEAK_DISCHARGE = "peak_discharge";
static final String PEAK_TIME = "peak_time";
static final String TOC = "time_of_concentration";
// internal result string.
private String[] res;
String st_name;
String toc;
/**
* Inclusive range check.
*
* @param val
* @param name
* @param min
* @param max
* @throws ServiceException
*/
static void checkRange(double val, String name, double min, double max) throws ServiceException {
if (val >= min && val <= max) {
return;
}
throw new ServiceException(name + ": " + val + " not in range " + min + " ... " + max);
}
static void checkGT(double val, String name, double min) throws ServiceException {
if (val > min) {
return;
}
throw new ServiceException(name + ": " + val + " must be > " + min);
}
@Override
protected void preProcess() throws Exception {
// watershed name
String ws_name = parameter().getString(WATERSHED_NAME);
st_name = parameter().getString(STORM_NAME);
double dr_area = parameter().getDouble(DRAINAGE_AREA);
// checkRange(dr_area, DRAINAGE_AREA, 0, 3.125);
// checkGT(dr_area, DRAINAGE_AREA, 0);
double cu_number = parameter().getDouble(CURVE_NUMBER);
// checkRange(cu_number, CURVE_NUMBER, 30, 100);
double ws_slope = parameter().getDouble(WATERSHED_SLOPE);
// checkRange(ws_slope, WATERSHED_SLOPE, 0.5, 64);
double ws_length = parameter().getDouble(WATERSHED_LENGTH);
// checkRange(ws_length, WATERSHED_LENGTH, 200, 26000);
double ra_depth = parameter().getDouble(RAIN_DEPTH);
// checkRange(ra_depth, RAIN_DEPTH, 0, 26);
String ra_dist = parameter().getString(RAIN_DIST);
// populate the input file
DecimalFormat f = new DecimalFormat("0.0####");
Map<String, Object> m = new HashMap<>();
m.put("wn", ws_name);
m.put("da______", pad(f.format(dr_area), 10));
m.put("cn______", pad(f.format(cu_number), 10));
m.put("wl______", pad(f.format(ws_length), 10));
m.put("ws______", pad(f.format(ws_slope), 10));
m.put("rde_____", pad(f.format(ra_depth), 10));
m.put("sn______", pad(st_name, 10));
m.put("rdi_____", pad(ra_dist, 10));
createTR20Input(m, new File(getWorkspaceDir(), "TR20.inp"));
}
@Override
protected void doProcess() throws Exception {
Executable tr20 = resources().getExe("tr20");
int ret = tr20.exec();
if (ret != 0) {
File err = tr20.stderr();
if (err.exists() && err.length() > 0) {
throw new ServiceException(FileUtils.readFileToString(err));
}
throw new ServiceException("Error executing TR20");
}
File err = new File(getWorkspaceDir(), "TR20.err");
if (err.exists() && err.length() > 0) {
throw new ServiceException(FileUtils.readFileToString(err));
}
File out = new File(getWorkspaceDir(), "TR20.out");
if (!out.exists()) {
throw new ServiceException("Missing File : TR20.out");
}
File dbg = new File(getWorkspaceDir(), "TR20.dbg");
if (!dbg.exists()) {
throw new ServiceException("Missing File : TR20.dbg");
}
res = parseOutputFile(st_name, out);
if (res == null || res.length != 3) {
throw new ServiceException("Missing output.");
}
toc = parseDebugFile(dbg);
if (toc == null) {
throw new ServiceException("Missing tocs.");
}
}
@Override
protected void postProcess() throws Exception {
results().put(RUNOFF_VOLUME, res[0], "Runoff depth output", "inch");
results().put(PEAK_TIME, res[1], "Peak Time", "h");
results().put(PEAK_DISCHARGE, res[2], "Unit Peak Discharge", "cfs/acre/inch");
results().put(TOC, toc, "Time of Concentration", "h");
}
static private String parseDebugFile(File debugFile) throws IOException {
List<String> out = FileUtils.readLines(debugFile);
for (int i = 0; i < out.size(); i++) {
String l = out.get(i);
if (l.trim().toLowerCase().startsWith("time of concentration =")) {
String[] vals = l.trim().split("\\s+");
return (vals.length == 5) ? vals[4] : null;
}
}
return null;
}
static private String[] parseOutputFile(String st_name, File outFile) throws IOException {
List<String> out = FileUtils.readLines(outFile);
int st_index = 0;
for (int i = 0; i < out.size(); i++) {
String l = out.get(i);
if (l.trim().startsWith("STORM " + st_name)) {
st_index = i;
break;
}
}
if (st_index > 0) {
for (int i = st_index; i < out.size(); i++) {
String l = out.get(i);
if (l.trim().startsWith("sub 1")) {
String[] vals = l.split("\\s+");
if (vals.length > 5) {
// System.out.println("runoff " + vals[3]);
// System.out.println("time " + vals[4]);
// System.out.println("rate " + vals[5]);
return new String[]{vals[3], vals[4], vals[5]};
}
break;
}
}
}
return null;
}
public static void createTR20Input(Map<String, Object> inp, File file) throws Exception {
Properties props = new Properties();
props.put("resource.loader", "class");
props.put("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
VelocityContext context = new VelocityContext(inp);
try (Writer writer = new FileWriter(file)) {
VelocityEngine ve = new VelocityEngine();
ve.init(props);
ve.getTemplate("m/hydrotools/efh2/V2_0_input.vm").merge(context, writer);
}
}
/**
*
* @param s
* @param len
* @return
*/
private static String pad(String s, int len) {
if (s.length() == len) {
return s;
}
if (s.length() > len) {
return s.substring(0, len);
}
return String.format("%1$-" + len + "s", s);
}
// public static void main(String[] args) throws IOException, Exception {
//// parseOutput("storm1", new File("/od/projects/csip-all/csip-hydrotools/src/java/bin/lin-amd64/TR20.out"));
//
// DecimalFormat formatter = new java.text.DecimalFormat("0000.00");
// float[] floats = new float[]{123.45f, 99.0f, 23.2f, 45.0f};
// String format = "%-10.1f";
//
// for (int i = 0; i < floats.length; i++) {
// float value = floats[i];
// System.out.println("'" + String.format(format, value) + "'");
// }
//
// Map<String, Object> m = new HashMap<>();
// m.put("wn", "ws name");
// m.put("da______", String.format(format, (float)1));
// m.put("cn______", String.format(format, (float)80));
// m.put("ws______", String.format(format, (float)3));
// m.put("wl______", String.format(format, (float)6000));
// m.put("sn______", pad("storm1",10));
// m.put("rde_____", String.format(format, (float)5));
// m.put("rdi_____", pad("TYPE NO_D", 10));
// createTR20Input(m, new File("/tmp", "TR20.inp"));
//
// System.out.println("'" + pad("storm", 10) + "'");
// System.out.println("'" + pad("s", 10) + "'");
// System.out.println("'" + pad("ssdddg", 10) + "'");
// System.out.println("'" + pad("stormchaserolaf", 10) + "'");
// }
}